import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { createAdminClient } from '@/lib/supabase/admin'
import { isMissingColumn } from '@/lib/db-compat'
import { publicOrigin } from '@/lib/app-url'
import { resolveBillingContext } from '@/lib/data/billing-context'
import { createPermitRequest } from '@/lib/data/permit-requests'
import { loadOwnDraft, requirePermitRequester, updateDraft } from '@/lib/data/permit-request-drafts'
import { logPurchaseEvent } from '@/lib/data/purchase-events'
import {
  getSynchronPayments,
  mockScenario,
  SynchronApiNotConfiguredError,
  SynchronUnavailableError,
} from '@/lib/integrations/synchron-payments'
import type { ServiceRequest } from '@/types/db'

const schema = z.object({
  draft_id: z.string().uuid(),
  // From the return URL when Synchron's page passes it back; otherwise the
  // draft row remembers the session it started.
  setup_reference: z.string().trim().min(1).max(200).optional(),
})

/**
 * Finish a Buy More Permits order after the customer comes back from
 * Synchron's card page (D4, task §9.2-9.3): re-check the card session with
 * Synchron, and only on a confirmed result create one permit request per
 * state — exactly what the one-step dialog did — carrying the confirmation
 * token through the gate. Idempotent: an already-submitted draft is a no-op.
 */
export async function POST(req: NextRequest, ctx: { params: Promise<{ id: string }> }) {
  const { id: tripId } = await ctx.params
  const access = await requirePermitRequester(tripId)
  if (!access.ok) return access.response
  const { user, participant } = access.guard
  const trip = access.trip

  const parsed = schema.safeParse(await req.json().catch(() => ({})))
  if (!parsed.success) return NextResponse.json({ error: 'Invalid request.' }, { status: 400 })

  const own = await loadOwnDraft(parsed.data.draft_id, tripId, user.id)
  if (!own.ok) return own.response
  const draft = own.draft
  if (draft.status === 'submitted') return NextResponse.json({ ok: true, requests: [], already_submitted: true })

  const setupReference = parsed.data.setup_reference ?? draft.setup_reference ?? null
  if (!setupReference) return NextResponse.json({ ok: false, status: 'no_setup' as const })

  const billing = await resolveBillingContext(user, trip)
  if (!billing.ok) return NextResponse.json({ error: 'Not authorized to bill for this trip.' }, { status: 403 })

  const adapter = getSynchronPayments({ scenario: mockScenario(req), appOrigin: publicOrigin(req) })
  let setup
  try {
    setup = await adapter.getCardSetupStatus(setupReference, billing.billing)
  } catch (error) {
    if (error instanceof SynchronApiNotConfiguredError || error instanceof SynchronUnavailableError) {
      await logPurchaseEvent({ event: 'synchron_api_error', tripId, actorUserId: user.id, outcome: 'failed', detail: { operation: 'getCardSetupStatus', reason: error.name } })
      return NextResponse.json({ ok: false, status: 'unavailable' as const })
    }
    throw error
  }

  if (setup.status === 'pending') {
    await updateDraft(draft.id, { readiness_status: 'setup_pending' })
    return NextResponse.json({ ok: false, status: 'pending' as const })
  }
  if (setup.status === 'failed' || !setup.paymentConfirmationToken) {
    await updateDraft(draft.id, { readiness_status: 'setup_failed' })
    return NextResponse.json({ ok: false, status: 'failed' as const })
  }
  const source = adapter.mode === 'mock' ? 'mock' : 'synchron'
  await logPurchaseEvent({
    event: 'card_use_confirmed',
    tripId,
    actorUserId: user.id,
    sourceSystem: source,
    synchronOrderToken: billing.billing.synchronOrderToken,
    detail: { draft_id: draft.id, setup_reference: setupReference, card: setup.card ? `${setup.card.brand} ${setup.card.last4}` : null },
  })

  // Same preconditions the one-step route enforces before inserting.
  const admin = createAdminClient()
  const { data: rateCon } = await admin.from('documents').select('id').eq('trip_id', tripId).eq('kind', 'rate_confirmation').limit(1)
  if (!rateCon?.length) return NextResponse.json({ error: 'Add the rate confirmation before requesting a permit.' }, { status: 400 })

  // Notes carry the attached file names, as the dialog always wrote them.
  let notes = draft.notes ?? ''
  if (draft.document_ids.length > 0) {
    const { data: docs } = await admin.from('documents').select('id,file_name').in('id', draft.document_ids)
    const names = ((docs ?? []) as Array<{ file_name: string | null }>).map((d) => d.file_name).filter((n): n is string => !!n)
    if (names.length > 0) notes = [notes, `Supporting docs: ${names.join(', ')}`].filter(Boolean).join('\n')
  }

  const label = participant.name || user.email || 'participant'
  const created: ServiceRequest[] = []
  const failed: string[] = []
  for (const stateCode of draft.state_codes) {
    const result = await createPermitRequest({
      tripId,
      trip,
      user: { id: user.id, email: user.email, name: user.name },
      participant,
      label,
      type: 'permit_request',
      stateCode: stateCode.toUpperCase(),
      notes: notes || null,
      routeType: null,
      notify: draft.notify ?? null,
      paidWith: null,
      payer: 'requester',
      replacedPermit: null,
      origin: publicOrigin(req),
    })
    if (result.ok) created.push(result.request)
    else failed.push(stateCode)
  }
  if (created.length === 0) return NextResponse.json({ error: 'Could not create the request.' }, { status: 500 })

  // Tell Synchron which card the order is billed against; store the safe reference.
  let paymentMethodRef: string | null = null
  try {
    const receipt = await adapter.submitPermitOrder({
      billing: billing.billing,
      serviceRequestIds: created.map((r) => r.id),
      stateCodes: created.map((r) => r.state_code ?? '').filter(Boolean),
      paymentConfirmationToken: setup.paymentConfirmationToken,
    })
    paymentMethodRef = receipt.paymentMethodRef
  } catch (error) {
    if (error instanceof SynchronApiNotConfiguredError || error instanceof SynchronUnavailableError) {
      await logPurchaseEvent({ event: 'synchron_api_error', tripId, actorUserId: user.id, outcome: 'failed', detail: { operation: 'submitPermitOrder', reason: error.name } })
    } else throw error
  }
  if (paymentMethodRef) {
    const { error } = await admin.from('service_requests').update({ payment_method_ref: paymentMethodRef }).in('id', created.map((r) => r.id))
    if (error && !isMissingColumn(error, 'payment_method_ref')) console.error('payment_method_ref update failed', error.message)
  }

  await updateDraft(draft.id, { status: 'submitted', readiness_status: 'card_on_file' })
  await logPurchaseEvent({
    event: 'permit_request_submitted',
    tripId,
    actorUserId: user.id,
    sourceSystem: source,
    synchronOrderToken: billing.billing.synchronOrderToken,
    detail: {
      draft_id: draft.id,
      request_ids: created.map((r) => r.id).join(','),
      states: created.map((r) => r.state_code).join(','),
      failed_states: failed.join(',') || null,
      payment_method_ref: paymentMethodRef,
    },
  })

  return NextResponse.json({ ok: true, requests: created.map((r) => ({ ...r, payment_method_ref: paymentMethodRef })), ...(failed.length ? { failed_states: failed } : {}) })
}
