import { readFileSync } from 'node:fs'
import path from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { createFakeSupabase } from './stubs/fake-supabase'
import { ACTOR, APP_ORIGIN, USER_ID, billingFor, seedCatalog, seedPermits, seedTrip } from './stubs/purchase-fixtures'
import type { PurchaseStatusReport } from '@/lib/integrations/synchron-payments/types'
import type { Purchase, PurchaseEvent, PurchaseItem } from '@/types/db'

/**
 * Tests F, G, L (docs/TASKS-2026-09-29-SYNCHRON-PURCHASING.md §13):
 * `paid` is set only by a VERIFIED confirmation, exactly once.
 */

const db = createFakeSupabase()
const mocks = vi.hoisted(() => ({
  logTripEvent: vi.fn(async () => {}),
  syncTripToHha: vi.fn(async () => ({ ok: true })),
  logOperationEvent: vi.fn(async () => {}),
}))
vi.mock('@/lib/supabase/admin', () => ({ createAdminClient: () => db }))
vi.mock('@/lib/audit', () => ({ logTripEvent: mocks.logTripEvent }))
vi.mock('@/lib/integrations/hha-trip-sync', () => ({ syncTripToHha: mocks.syncTripToHha }))
vi.mock('@/lib/observability/operation-events', () => ({ logOperationEvent: mocks.logOperationEvent }))

const { addCartItem, applyVerifiedPaymentEvent, createPurchaseFromCart, loadPurchaseByReference, requestCheckout } = await import('@/lib/data/purchases')
const { resolveRoutingProduct } = await import('@/lib/data/routing-products')
const { returnStateFor } = await import('@/lib/domain/purchases')
const { synchronPaymentsMode, isTrustedPaymentSource } = await import('@/lib/integrations/synchron-payments')

beforeEach(() => {
  db.reset()
  seedCatalog(db)
})
afterEach(() => { vi.clearAllMocks(); vi.unstubAllEnvs() })

/** Cart → purchase → mock checkout, so the purchase sits in `pending_payment`. */
async function pendingPaymentPurchase(states: string[]) {
  vi.stubEnv('SYNCHRON_PAYMENTS_MODE', 'mock')
  const trip = seedTrip(db, { synchron_order_token: 'ABC12345' })
  const permits = seedPermits(db, trip.id, states)
  const resolved = await resolveRoutingProduct('express_route')
  if (!resolved.ok) throw new Error(resolved.reason)
  for (const p of permits) {
    const added = await addCartItem({ trip, ...ACTOR, permitId: p.id as string, productCode: 'express_route', product: resolved.product })
    if (!added.ok) throw new Error(added.error)
  }
  const created = await createPurchaseFromCart({ trip, ...ACTOR, billing: billingFor(trip), companyId: null })
  if (!created.ok) throw new Error(created.error)
  const checkout = await requestCheckout({ bundle: created.bundle, billing: billingFor(trip), actorUserId: USER_ID, actorLabel: ACTOR.actorLabel, appOrigin: APP_ORIGIN, scenario: 'checkout_created' })
  if (!checkout.ok) throw new Error(checkout.error)
  const bundle = await loadPurchaseByReference(created.bundle.purchase.purchase_reference)
  if (!bundle) throw new Error('purchase vanished')
  expect(bundle.purchase.purchase_status).toBe('pending_payment')
  return bundle
}

function paidReport(reference: string, eventId = 'evt_synchron_0001'): PurchaseStatusReport {
  return {
    purchaseReference: reference,
    paymentStatus: 'paid',
    synchronPurchaseToken: 'sp_123',
    stripeCheckoutSessionId: 'cs_live_123',
    stripePaymentIntentId: 'pi_live_123',
    stripeChargeId: 'ch_live_123',
    paymentConfirmedAt: '2026-09-29T12:30:00.000Z',
    externalEventId: eventId,
  }
}

const purchaseRow = () => db.table('purchases')[0] as unknown as Purchase
const items = () => db.table('purchase_items') as unknown as PurchaseItem[]
const events = (name: string) => (db.table('purchase_events') as unknown as PurchaseEvent[]).filter((e) => e.event === name)

describe('Test F — the return page never changes payment status', () => {
  const dir = path.resolve(__dirname, '../src/app/purchases/[ref]/return')

  it.each(['page.tsx', 'return-status.tsx'])('%s does not verify payments or write a purchase status', (file) => {
    const source = readFileSync(path.join(dir, file), 'utf8')
    expect(source).not.toMatch(/applyVerifiedPaymentEvent/)
    expect(source).not.toMatch(/reconcilePurchase\b/)
    // No direct write of a payment status: no `purchase_status: '...'` literal
    // and no update against the purchases table.
    expect(source).not.toMatch(/purchase_status\s*:\s*['"]/)
    expect(source).not.toMatch(/from\(['"]purchases['"]\)/)
    expect(source).not.toMatch(/['"]paid['"]\s*[,)]/)
  })

  it('page.tsx may only cancel, through cancelPurchase, and the client only reads', () => {
    const page = readFileSync(path.join(dir, 'page.tsx'), 'utf8')
    expect(page).toMatch(/cancelPurchase\(/)
    expect(page).toMatch(/query\.cancelled === '1'/)
    const client = readFileSync(path.join(dir, 'return-status.tsx'), 'utf8')
    expect(client.startsWith("'use client'")).toBe(true)
    expect(client.match(/method:\s*'POST'/g)?.length).toBe(2) // reconcile + retry checkout, both server-verified
    expect(client).not.toMatch(/method:\s*'(PATCH|PUT|DELETE)'/)
  })

  it('loading the purchase for the return view leaves it pending_payment', async () => {
    const bundle = await pendingPaymentPurchase(['TX'])
    const before = { ...purchaseRow() }
    const loaded = await loadPurchaseByReference(bundle.purchase.purchase_reference)
    expect(loaded?.purchase.purchase_status).toBe('pending_payment')
    expect(returnStateFor('pending_payment', { delayed: false })).toBe('pending')
    expect(returnStateFor('pending_payment', { delayed: true })).toBe('delayed')
    expect(purchaseRow()).toEqual(before)
    expect(events('purchase_marked_paid')).toHaveLength(0)
  })
})

describe('Test G — only a trusted source can mark paid', () => {
  it('rejects a mock confirmation when mock mode is off', async () => {
    const bundle = await pendingPaymentPurchase(['TX'])
    vi.unstubAllEnvs()
    expect(process.env.SYNCHRON_PAYMENTS_MODE).toBeUndefined()
    expect(synchronPaymentsMode()).toBe('live')
    expect(isTrustedPaymentSource('mock')).toBe(false)

    const result = await applyVerifiedPaymentEvent({ source: 'mock', report: paidReport(bundle.purchase.purchase_reference), via: 'callback' })
    expect(result).toEqual({ ok: false, reason: 'untrusted_source' })
    expect(purchaseRow().purchase_status).toBe('pending_payment')
    expect(items().every((i) => i.fulfillment_status === 'awaiting_payment')).toBe(true)
    expect(db.table('service_requests')).toHaveLength(0)
    expect(events('synchron_api_error')[0]?.detail).toMatchObject({ reason: 'untrusted_source' })
  })

  it('a synchron confirmation marks paid and releases one route request per item', async () => {
    const bundle = await pendingPaymentPurchase(['TX', 'OK'])
    db.seed('profiles', [{ id: USER_ID, full_name: 'Dana Dispatcher', email: 'dana@carrier.example', source_token: 'USERTOKEN1234567' }])
    const result = await applyVerifiedPaymentEvent({ source: 'synchron', report: paidReport(bundle.purchase.purchase_reference), via: 'callback' })
    expect(result).toEqual({ ok: true, changed: true, status: 'paid' })
    expect(purchaseRow().purchase_status).toBe('paid')

    const requests = db.table('service_requests')
    expect(requests).toHaveLength(2)
    expect(requests.every((r) => r.type === 'route_request' && r.paid_with === 'card' && r.requested_by === USER_ID)).toBe(true)
    expect(requests.map((r) => r.state_code).sort()).toEqual(['OK', 'TX'])
    expect(requests.every((r) => r.vendor_order_id === 'ABC12345' && r.status === 'in_progress')).toBe(true)
    expect(requests.every((r) => r.requester_label === 'Dana Dispatcher')).toBe(true)

    const released = items()
    expect(released.every((i) => i.fulfillment_status === 'submitted_to_synchron')).toBe(true)
    expect(new Set(released.map((i) => i.service_request_id))).toEqual(new Set(requests.map((r) => r.id)))

    const ref = db.table('payment_references')
    expect(ref).toHaveLength(1)
    expect(ref[0]).toMatchObject({ payment_status: 'paid', source_system: 'synchron', stripe_payment_intent_id: 'pi_live_123', payment_confirmed_at: '2026-09-29T12:30:00.000Z' })
    expect(mocks.logTripEvent).toHaveBeenCalledTimes(2)
    expect(mocks.syncTripToHha).toHaveBeenCalledWith(bundle.purchase.hha_trip_id)
  })

  it('production always resolves to the live adapter, whatever the env says', () => {
    vi.stubEnv('NODE_ENV', 'production')
    vi.stubEnv('SYNCHRON_PAYMENTS_MODE', 'mock')
    expect(synchronPaymentsMode()).toBe('live')
    expect(isTrustedPaymentSource('mock')).toBe(false)
    expect(isTrustedPaymentSource('synchron')).toBe(true)
  })

  it('paid is not reachable from pending_checkout: the purchase is flagged for reconciliation', async () => {
    vi.stubEnv('SYNCHRON_PAYMENTS_MODE', 'mock')
    const trip = seedTrip(db)
    const [tx] = seedPermits(db, trip.id, ['TX'])
    const resolved = await resolveRoutingProduct('express_route')
    if (!resolved.ok) throw new Error(resolved.reason)
    await addCartItem({ trip, ...ACTOR, permitId: tx.id as string, productCode: 'express_route', product: resolved.product })
    const created = await createPurchaseFromCart({ trip, ...ACTOR, billing: billingFor(trip), companyId: null })
    if (!created.ok) throw new Error(created.error)

    const result = await applyVerifiedPaymentEvent({ source: 'synchron', report: paidReport(created.bundle.purchase.purchase_reference), via: 'callback' })
    expect(result).toEqual({ ok: false, reason: 'bad_transition' })
    expect(purchaseRow()).toMatchObject({ purchase_status: 'pending_checkout', needs_reconciliation: true })
    expect(db.table('service_requests')).toHaveLength(0)
  })
})

describe('Test L — a repeated delivery is a no-op', () => {
  it('same externalEventId twice → one transition, one service request per item, one paid event', async () => {
    const bundle = await pendingPaymentPurchase(['TX', 'OK'])
    const report = paidReport(bundle.purchase.purchase_reference, 'evt_dup_42')
    const first = await applyVerifiedPaymentEvent({ source: 'synchron', report, via: 'callback' })
    expect(first).toEqual({ ok: true, changed: true, status: 'paid' })
    const second = await applyVerifiedPaymentEvent({ source: 'synchron', report, via: 'reconcile' })
    expect(second).toEqual({ ok: false, reason: 'duplicate' })

    expect(purchaseRow().purchase_status).toBe('paid')
    expect(db.table('service_requests')).toHaveLength(2)
    expect(items()).toHaveLength(2)
    expect(items().every((i) => i.fulfillment_status === 'submitted_to_synchron' && i.service_request_id)).toBe(true)
    expect(events('purchase_marked_paid')).toHaveLength(1)
    expect(events('synchron_payment_verified')).toHaveLength(1)
    expect(events('duplicate_event_ignored')).toHaveLength(1)
    expect(events('routing_item_released')).toHaveLength(2)
    expect(mocks.syncTripToHha).toHaveBeenCalledTimes(1)
  })
})
