import { describe, expect, it } from 'vitest'
import {
  canEditPermitCost, canSeePermitCost, countableCostPermits, displayPermitCost, effectiveCost, feeCandidates,
  permitFeesForAi, readPermitCost, readSuppliedCost, stripPermitCost, stripPermitCostFor, tripPermitFees,
  PERMIT_COST_DISCLAIMER, TOTAL_COMPLETE_LABEL, TOTAL_INCOMPLETE_LABEL,
} from '@/lib/domain/permit-cost'
import type { Permit } from '@/types/db'

/**
 * Permit cost (Nash, 2026-09-24): the fee the issuing state charged, never the
 * permitting company's service charge. Money and visibility, so pinned here.
 */
const permit = (over: Partial<Permit> & { id: string }) => ({ state_code: 'TX', permit_number: null, created_at: '2026-09-24T00:00:00Z', permit_cost_currency: 'USD', ...over }) as Permit

describe('reading the fee off a permit', () => {
  it('takes a clearly labelled state fee with high confidence', () => {
    const r = readPermitCost('TEXAS OVERSIZE PERMIT\nPermit No 12345\nPermit Fee: $125.00\nIssued 09/24/2026')
    expect(r).toMatchObject({ amount: 125, status: 'found', confidence: 'high' })
    expect(r.extractedText).toBe('$125.00')
  })
  it('reads the amount with or without a dollar sign', () => {
    expect(readPermitCost('State Fee 68.00').amount).toBe(68)
    expect(readPermitCost('PERMIT FEE $1,250.50').amount).toBe(1250.5)
  })
  it('ignores money that is plainly not the permit fee', () => {
    const r = readPermitCost('Surety Bond: $10,000.00\nInsurance coverage $1,000,000\nCredit card fee $3.50\nPermit Fee: $92.00')
    expect(r).toMatchObject({ amount: 92, status: 'found', confidence: 'high' })
  })
  it('never guesses when several different fees are labelled', () => {
    const r = readPermitCost('Permit Fee: $50.00\nState Fee: $75.00')
    expect(r).toMatchObject({ amount: null, status: 'needs_review', confidence: 'low' })
  })
  it('accepts a total that covers several line fees', () => {
    const r = readPermitCost('Permit Fee: $50.00\nPermit Fee: $25.00\nTotal Fee: $75.00')
    expect(r).toMatchObject({ amount: 75, status: 'found', confidence: 'medium' })
  })
  it('a weakly labelled single amount is medium confidence', () => {
    expect(readPermitCost('Amount Paid: $48.00')).toMatchObject({ amount: 48, status: 'found', confidence: 'medium' })
  })
  it('finds nothing when there is nothing to find', () => {
    expect(readPermitCost('OVERSIZE LOAD PERMIT — no fee shown')).toMatchObject({ amount: null, status: 'not_found', confidence: null })
    expect(readPermitCost('')).toMatchObject({ status: 'not_found' })
    expect(readPermitCost(null)).toMatchObject({ status: 'not_found' })
  })
  it('a bare number with no money label is not a fee', () => {
    expect(feeCandidates('Gross weight 118,000 lb\nHeight 14.08')).toEqual([])
  })
  it('a trusted supplied value is taken as given', () => {
    expect(readSuppliedCost(125)).toMatchObject({ amount: 125, status: 'found', confidence: 'high' })
    expect(readSuppliedCost('$68.00')).toMatchObject({ amount: 68 })
    expect(readSuppliedCost('not a number')).toBeNull()
    expect(readSuppliedCost(null)).toBeNull()
  })
})

describe('what the user sees', () => {
  it('a missing fee never shows as $0.00', () => {
    const d = displayPermitCost({ permit_cost_amount: null, permit_cost_status: 'not_found' })
    expect(d.text).toBe('—')
    expect(d.hint).toContain('not found')
  })
  it('needs review says so instead of a number', () => {
    expect(displayPermitCost({ permit_cost_status: 'needs_review', permit_cost_amount: null }).needsReview).toBe(true)
  })
  it('a manual correction wins over the extracted amount', () => {
    const p = { permit_cost_amount: 120, permit_cost_manual_override: 102, permit_cost_status: 'corrected' as const }
    expect(effectiveCost(p)).toBe(102)
    expect(displayPermitCost(p).text).toBe('$102.00')
  })
  it('a real fee formats as money', () => {
    expect(displayPermitCost({ permit_cost_amount: 125, permit_cost_status: 'found' }).text).toBe('$125.00')
  })
})

describe('who may see and edit it', () => {
  it('pilots never see permit cost, whatever access they hold', () => {
    expect(canSeePermitCost('pilot')).toBe(false)
    expect(canSeePermitCost(null)).toBe(false)
    for (const r of ['broker', 'dispatcher', 'driver', 'admin']) expect(canSeePermitCost(r)).toBe(true)
  })
  it('the fields are removed from the payload, not just hidden', () => {
    const p = permit({ id: 'p1', permit_cost_amount: 125, permit_cost_status: 'found', permit_number: 'TX-1' })
    const stripped = stripPermitCost(p as unknown as Record<string, unknown>)
    expect('permit_cost_amount' in stripped).toBe(false)
    expect('permit_cost_status' in stripped).toBe(false)
    expect(stripped.permit_number).toBe('TX-1')
    expect(stripPermitCostFor('pilot', [p as unknown as Record<string, unknown>])[0]).not.toHaveProperty('permit_cost_amount')
    expect(stripPermitCostFor('broker', [p as unknown as Record<string, unknown>])[0]).toHaveProperty('permit_cost_amount')
  })
  it('a driver reads the fee but does not change it', () => {
    expect(canEditPermitCost({ role: 'driver', isInternal: false })).toBe(false)
    expect(canEditPermitCost({ role: 'broker', isInternal: false })).toBe(true)
    expect(canEditPermitCost({ role: 'dispatcher', isInternal: false })).toBe(true)
    expect(canEditPermitCost({ role: 'pilot', isInternal: false })).toBe(false)
    expect(canEditPermitCost({ role: 'driver', isInternal: true })).toBe(true)
  })
})

describe('the trip total', () => {
  const found = (id: string, state: string, amount: number, number?: string) => permit({ id, state_code: state, permit_number: number ?? null, permit_cost_amount: amount, permit_cost_status: 'found' })

  it('adds up and calls itself complete (acceptance test §22)', () => {
    const fees = tripPermitFees([found('1', 'TX', 125), found('2', 'NM', 48), found('3', 'AZ', 36), found('4', 'OH', 92)])
    expect(fees.total).toBe(301)
    expect(fees.complete).toBe(true)
    expect(fees.label).toBe(TOTAL_COMPLETE_LABEL)
    expect(fees.warning).toBeNull()
  })
  it('says Known Permit Fees and warns when one is missing (acceptance test §23)', () => {
    const fees = tripPermitFees([found('1', 'TX', 125), found('2', 'NM', 48), permit({ id: '3', state_code: 'AZ', permit_cost_status: 'not_found' })])
    expect(fees.total).toBe(173)
    expect(fees.complete).toBe(false)
    expect(fees.label).toBe(TOTAL_INCOMPLETE_LABEL)
    expect(fees.warning).toBe('Permit fee was not detected for 1 permit. The actual trip permit total may be higher.')
    expect(fees.lines.find((l) => l.state === 'AZ')).toMatchObject({ text: '—', counted: false })
  })
  it('a permit needing review does not silently count as zero', () => {
    const fees = tripPermitFees([found('1', 'TX', 125), permit({ id: '2', state_code: 'NM', permit_cost_status: 'needs_review', permit_cost_amount: 999 })])
    expect(fees.total).toBe(125)
    expect(fees.missing).toBe(1)
    expect(fees.complete).toBe(false)
  })
  it('a revision that replaces a permit is not charged twice (§11)', () => {
    const original = found('1', 'TX', 125, 'TX-900')
    const revision = permit({ id: '2', state_code: 'TX', permit_number: 'TX-900R', permit_cost_amount: 125, permit_cost_status: 'found', supersedes_permit_id: '1', created_at: '2026-09-25T00:00:00Z' })
    const fees = tripPermitFees([original, revision])
    expect(fees.total).toBe(125)
    expect(fees.counted).toBe(1)
    expect(fees.notCounted).toBe(1)
  })
  it('the same permit uploaded twice counts once, keeping the newer copy', () => {
    const first = permit({ id: '1', state_code: 'TX', permit_number: 'TX-900', permit_cost_amount: 125, permit_cost_status: 'found', created_at: '2026-09-24T00:00:00Z' })
    const again = permit({ id: '2', state_code: 'tx', permit_number: ' tx-900 ', permit_cost_amount: 125, permit_cost_status: 'found', created_at: '2026-09-25T00:00:00Z' })
    const { countable } = countableCostPermits([first, again])
    expect(countable.map((p) => p.id)).toEqual(['2'])
    expect(tripPermitFees([first, again]).total).toBe(125)
  })
  it('permits without a number are never merged together', () => {
    const fees = tripPermitFees([found('1', 'TX', 125), found('2', 'TX', 125)])
    expect(fees.counted).toBe(2)
    expect(fees.total).toBe(250)
  })
  it('a permit marked as not counting is left out', () => {
    const fees = tripPermitFees([found('1', 'TX', 125), permit({ id: '2', state_code: 'NM', permit_cost_amount: 48, permit_cost_status: 'found', cost_counts_toward_total: false })])
    expect(fees.total).toBe(125)
    expect(fees.notCounted).toBe(1)
  })
  it('no permits at all is not a complete total of zero', () => {
    const fees = tripPermitFees([])
    expect(fees.total).toBe(0)
    expect(fees.complete).toBe(false)
    expect(fees.counted).toBe(0)
  })
})

describe('what the AI is allowed to say', () => {
  it('quotes the total and always adds the disclaimer', () => {
    const fees = tripPermitFees([permit({ id: '1', state_code: 'TX', permit_cost_amount: 125, permit_cost_status: 'found' })])
    const line = permitFeesForAi(fees)
    expect(line).toContain('$125.00')
    expect(line).toContain(PERMIT_COST_DISCLAIMER)
  })
  it('says plainly when nothing is known', () => {
    expect(permitFeesForAi(tripPermitFees([]))).toContain('No state permit fee has been detected')
  })
  it('carries the incomplete warning into the answer', () => {
    const fees = tripPermitFees([permit({ id: '1', state_code: 'TX', permit_cost_amount: 125, permit_cost_status: 'found' }), permit({ id: '2', state_code: 'AZ', permit_cost_status: 'not_found' })])
    expect(permitFeesForAi(fees)).toContain('may be higher')
  })
})
