import { describe, it, expect } from 'vitest'
import {
  findAutoRuleMatch,
  findHighlightMatch,
  formatCcPasteBlock,
  parseCcLine,
  parseCcPaste,
  parseCurrency,
  parseExpenseDate,
} from '@/lib/cc-expenses'

describe('parseExpenseDate', () => {
  it('CCE-D-001 long format "May 20, 2026" → 2026-05-20', () => {
    expect(parseExpenseDate('May 20, 2026')).toBe('2026-05-20')
  })
  it('CCE-D-002 ISO YYYY-MM-DD passes through', () => {
    expect(parseExpenseDate('2026-05-20')).toBe('2026-05-20')
  })
  it('CCE-D-003 mixed-case month + no comma still parses', () => {
    expect(parseExpenseDate('may 20 2026')).toBe('2026-05-20')
    expect(parseExpenseDate('MAY 20 2026')).toBe('2026-05-20')
  })
  it('CCE-D-004 returns null on garbage', () => {
    expect(parseExpenseDate('nonsense')).toBeNull()
    expect(parseExpenseDate('')).toBeNull()
    expect(parseExpenseDate('Foobar 20, 2026')).toBeNull()
  })
  it('CCE-D-005 pads single-digit days', () => {
    expect(parseExpenseDate('Jan 5, 2026')).toBe('2026-01-05')
  })
})

describe('parseCurrency', () => {
  it('CCE-C-001 strips $ and commas', () => {
    expect(parseCurrency('$7.15')).toBe(7.15)
    expect(parseCurrency('$3,856.56')).toBe(3856.56)
    expect(parseCurrency('1234')).toBe(1234)
  })
  it('CCE-C-002 returns null on empty / garbage', () => {
    expect(parseCurrency('')).toBeNull()
    expect(parseCurrency('   ')).toBeNull()
    expect(parseCurrency('abc')).toBeNull()
  })
  it('CCE-C-003 rounds to 2 decimals', () => {
    expect(parseCurrency('$0.999')).toBe(1)
    expect(parseCurrency('$10.234')).toBe(10.23)
  })
})

describe('parseCcLine', () => {
  it('CCE-L-001 standard 5-column line with trailing tab', () => {
    const r = parseCcLine(
      'May 20, 2026\tFACEBK *N7VKLP9RX2\t$7.15\t\t$3,856.56\t',
    )
    expect(r).not.toBeNull()
    expect(r?.expense_date).toBe('2026-05-20')
    expect(r?.description).toBe('FACEBK *N7VKLP9RX2')
    expect(r?.amount_usd).toBe(7.15)
    expect(r?.balance_usd).toBe(3856.56)
  })
  it('CCE-L-002 credit column when debit is blank', () => {
    const r = parseCcLine('May 20, 2026\tREFUND\t\t$50.00\t$3,000.00')
    expect(r?.amount_usd).toBe(50)
  })
  it('CCE-L-003 empty line → null', () => {
    expect(parseCcLine('')).toBeNull()
    expect(parseCcLine('   ')).toBeNull()
  })
  it('CCE-L-004 keeps the raw line for audit', () => {
    const raw = 'May 20, 2026\tFOO\t$1.00'
    expect(parseCcLine(raw)?.raw_line).toBe(raw)
  })
  it('CCE-L-005 partial parse: unknown date keeps other fields', () => {
    const r = parseCcLine('???\tDESC\t$5.00')
    expect(r?.expense_date).toBeNull()
    expect(r?.description).toBe('DESC')
    expect(r?.amount_usd).toBe(5)
  })
})

describe('parseCcPaste', () => {
  it('CCE-P-001 splits on newlines, skips blanks', () => {
    const input =
      'May 20, 2026\tA\t$1\n\nMay 19, 2026\tB\t$2\n   \nMay 18, 2026\tC\t$3'
    const r = parseCcPaste(input)
    expect(r).toHaveLength(3)
    expect(r.map((p) => p.description)).toEqual(['A', 'B', 'C'])
  })
  it('CCE-P-002 handles CRLF line endings', () => {
    const input = 'May 20, 2026\tA\t$1\r\nMay 19, 2026\tB\t$2'
    expect(parseCcPaste(input)).toHaveLength(2)
  })
})

describe('findHighlightMatch', () => {
  const kws = [{ keyword: 'UBER' }, { keyword: 'FACEBK' }]
  it('CCE-H-001 returns matched keyword on substring contains', () => {
    expect(findHighlightMatch('UBER *EATS', kws)).toBe('UBER')
    expect(findHighlightMatch('FACEBK *N7VKLP9RX2', kws)).toBe('FACEBK')
  })
  it('CCE-H-002 case-insensitive', () => {
    expect(findHighlightMatch('uber *eats', kws)).toBe('UBER')
  })
  it('CCE-H-003 returns null on no match / null / empty desc', () => {
    expect(findHighlightMatch('GOOGLE *ADS', kws)).toBeNull()
    expect(findHighlightMatch(null, kws)).toBeNull()
    expect(findHighlightMatch('', kws)).toBeNull()
  })
  it('CCE-H-004 first-in-list wins on multi-match', () => {
    expect(findHighlightMatch('UBER FACEBK', kws)).toBe('UBER')
  })
  it('CCE-H-005 empty-keyword entry is skipped', () => {
    expect(
      findHighlightMatch('FACEBK', [{ keyword: '' }, { keyword: 'FACEBK' }]),
    ).toBe('FACEBK')
  })
})

describe('findAutoRuleMatch', () => {
  const rule = {
    id: 'r1',
    keyword: 'ADS9772464823',
    project_id: 'p1',
    assignment_description: 'Google',
    category: 'Client Advertising Spend',
  }
  it('CCE-R-001 returns the matched rule with all fields', () => {
    const r = findAutoRuleMatch('GOOGLE *ADS9772464823', [rule])
    expect(r).toEqual(rule)
  })
  it('CCE-R-002 case-insensitive substring contains', () => {
    const r = findAutoRuleMatch('google *ads9772464823', [rule])
    expect(r?.id).toBe('r1')
  })
  it('CCE-R-003 returns null when no rule matches', () => {
    expect(findAutoRuleMatch('FACEBK *XYZ', [rule])).toBeNull()
  })
  it('CCE-R-004 first-match wins (caller controls order)', () => {
    const r2 = { ...rule, id: 'r2', keyword: 'GOOGLE' }
    // r2 (matches "GOOGLE" substring) is first; should win even though
    // r1 would also match.
    const r = findAutoRuleMatch('GOOGLE *ADS9772464823', [r2, rule])
    expect(r?.id).toBe('r2')
  })
  it('CCE-R-005 null / empty description → null', () => {
    expect(findAutoRuleMatch(null, [rule])).toBeNull()
    expect(findAutoRuleMatch('', [rule])).toBeNull()
  })
})

describe('formatCcPasteBlock', () => {
  it('CCE-X-001 empty input → empty string', () => {
    expect(formatCcPasteBlock([])).toBe('')
  })
  it('CCE-X-002 7-column shape per assigned line', () => {
    const out = formatCcPasteBlock([
      {
        client: 'Tuscany',
        project: 'Monthly Services',
        assignment_description: 'Google',
        amount_usd: 750,
        expense_date: '2026-05-18',
        category: 'Client Advertising Spend',
      },
    ])
    expect(out.split('\t')).toEqual([
      'Tuscany : Monthly Services',
      'Google',
      '750.00',
      '2026-05-18',
      '',
      'Bowden Works',
      'Client Advertising Spend',
    ])
  })
  it('CCE-X-003 amount has 2 decimal places, no $', () => {
    const out = formatCcPasteBlock([
      {
        client: 'C',
        project: 'P',
        assignment_description: 'D',
        amount_usd: 1234.5,
        expense_date: '2026-01-01',
        category: 'X',
      },
    ])
    expect(out).toContain('\t1234.50\t')
    expect(out).not.toContain('$')
  })
  it('CCE-X-004 multiple lines joined with newlines', () => {
    const out = formatCcPasteBlock([
      {
        client: 'A',
        project: 'P1',
        assignment_description: 'D1',
        amount_usd: 10,
        expense_date: '2026-01-01',
        category: 'C1',
      },
      {
        client: 'B',
        project: 'P2',
        assignment_description: 'D2',
        amount_usd: 20,
        expense_date: '2026-01-02',
        category: 'C2',
      },
    ])
    expect(out.split('\n')).toHaveLength(2)
  })
})
