import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { createAdminClient } from '@/lib/supabase/admin'
import { publicOrigin } from '@/lib/app-url'
import { extractPermit, extractRateConfirmation, createSynchronOrder } from '@/lib/adapters/flask'
import { generateRefCode } from '@/lib/domain/refcode'
import { computePermitWarnings } from '@/lib/domain/warnings'
import { logTripEvent } from '@/lib/audit'
import { isMissingColumn } from '@/lib/db-compat'
import type { PermitSource } from '@/types/db'

/**
 * Public broker-intake submission (no login required).
 * Creates: intake_submission → trip → participants (broker owner + dispatcher)
 * → invitation → documents (+ permits & warnings when the upload path is used,
 * or a Synchron permit request when the purchase path is used).
 */

export const runtime = 'nodejs'
export const maxDuration = 120

const MAX_FILE_BYTES = 25 * 1024 * 1024
const ALLOWED_MIME = new Set([
  'application/pdf',
  'image/jpeg',
  'image/png',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
])

const fieldsSchema = z.object({
  slug: z.string().min(1),
  carrier_company: z.string().trim().min(1).max(200),
  contact_name: z.string().trim().min(1).max(200),
  contact_email: z.string().trim().email().max(320),
  contact_phone: z.string().trim().min(5).max(40),
  contact_phone_ext: z.string().trim().max(20).optional().or(z.literal('')),
  notes: z.string().trim().max(2000).optional().or(z.literal('')),
  permit_source: z.enum(['upload', 'synchron']),
  // Broker's decision when creating a trip request (Task 15):
  // force Synchron purchase or allow the carrier's own permits.
  permit_policy: z.enum(['synchron_required', 'upload_allowed']).optional(),
  // Synchron flow only: who pays Synchron for permit processing. Required
  // with permit_policy=synchron_required (checked below, not in the schema,
  // so the public carrier intake stays unaffected).
  payment_responsible_party: z.enum(['broker', 'carrier']).optional(),
  website: z.string().max(0).optional().or(z.literal('')), // honeypot
})

// naive in-memory rate limit (per instance) — replaced by edge limits later
const recent = new Map<string, number[]>()
function rateLimited(key: string): boolean {
  const now = Date.now()
  const hits = (recent.get(key) ?? []).filter((t) => now - t < 60_000)
  hits.push(now)
  recent.set(key, hits)
  return hits.length > 10
}

function validFile(f: File): boolean {
  return f.size > 0 && f.size <= MAX_FILE_BYTES && ALLOWED_MIME.has(f.type)
}

export async function POST(req: NextRequest) {
  const ip = req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? 'unknown'
  if (rateLimited(ip)) {
    return NextResponse.json({ error: 'Too many submissions. Try again shortly.' }, { status: 429 })
  }

  const form = await req.formData()
  const parsed = fieldsSchema.safeParse({
    slug: form.get('slug'),
    permit_policy: form.get('permit_policy') || undefined,
    payment_responsible_party: form.get('payment_responsible_party') || undefined,
    carrier_company: form.get('carrier_company'),
    contact_name: form.get('contact_name'),
    contact_email: form.get('contact_email'),
    contact_phone: form.get('contact_phone'),
    contact_phone_ext: form.get('contact_phone_ext') ?? '',
    notes: form.get('notes') ?? '',
    permit_source: form.get('permit_source'),
    website: form.get('website') ?? '',
  })
  if (!parsed.success) {
    return NextResponse.json(
      { error: parsed.error.issues[0]?.message ?? 'Invalid submission' },
      { status: 400 },
    )
  }
  const input = parsed.data
  if (input.website) {
    // honeypot filled — pretend success, store nothing
    return NextResponse.json({ ok: true })
  }

  const rateCon = form.get('rate_confirmation')
  if (!(rateCon instanceof File) || !validFile(rateCon)) {
    return NextResponse.json(
      { error: 'A rate confirmation file (PDF/JPG/PNG/DOCX, max 25MB) is required.' },
      { status: 400 },
    )
  }
  const permitFiles = form
    .getAll('permits')
    .filter((f): f is File => f instanceof File && validFile(f))

  const admin = createAdminClient()

  // 1. broker page
  const { data: page } = await admin
    .from('broker_pages')
    .select('*')
    .eq('slug', input.slug)
    .eq('enabled', true)
    .maybeSingle()
  if (!page) {
    return NextResponse.json({ error: 'This intake page is not available.' }, { status: 404 })
  }
  const { data: owner } = await admin
    .from('profiles')
    .select('*')
    .eq('id', page.owner_id)
    .single()

  // 2. try to extract rate-con details via the existing Flask extractor
  const extraction = await extractRateConfirmation(rateCon, rateCon.name)
  const rc = extraction.ok ? extraction.data! : undefined

  // The broker cannot submit a Synchron purchase request without choosing
  // who pays (source doc §8).
  if (input.permit_policy === 'synchron_required' && !input.payment_responsible_party) {
    return NextResponse.json(
      { error: 'Choose who will pay Synchron Permits for this permit order.' },
      { status: 400 },
    )
  }

  // 3. create the trip
  const permitSource = input.permit_source as PermitSource
  const { data: trip, error: tripError } = await admin
    .from('trips')
    .insert({
      ref_code: generateRefCode(),
      created_by: null,
      broker_page_id: page.id,
      status: 'waiting_for_permits',
      permit_source: permitSource,
      permit_policy: input.permit_policy ?? null,
      // Only sent when chosen — keeps rows working until migration 0008 runs.
      ...(input.payment_responsible_party
        ? { payment_responsible_party: input.payment_responsible_party }
        : {}),
      carrier_name: input.carrier_company,
      origin: rc?.origin ?? '',
      destination: rc?.destination ?? '',
      commodity: rc?.commodity ?? '',
      load_length_in: rc?.length_in ?? null,
      load_width_in: rc?.width_in ?? null,
      load_height_in: rc?.height_in ?? null,
      load_weight_lbs: rc?.weight_lbs ?? null,
      pickup_date: rc?.pickup_date ?? null,
      delivery_date: rc?.delivery_date ?? null,
      notes: input.notes || null,
    })
    .select()
    .single()
  if (tripError || !trip) {
    return NextResponse.json({ error: 'Could not create the trip. Try again.' }, { status: 500 })
  }

  // Everyone on the trip sees who was chosen to pay Synchron (source doc §24).
  if (input.payment_responsible_party) {
    await logTripEvent({
      tripId: trip.id,
      actorId: page.owner_id,
      actorLabel: owner?.full_name ?? page.display_name,
      action: 'payment_responsibility_selected',
      detail: { party: input.payment_responsible_party },
    })
  }

  // 4. intake submission record
  await admin.from('intake_submissions').insert({
    broker_page_id: page.id,
    trip_id: trip.id,
    carrier_company: input.carrier_company,
    contact_name: input.contact_name,
    contact_email: input.contact_email,
    contact_phone: input.contact_phone,
    permit_source: permitSource,
    notes: input.notes || null,
    source_ip: ip,
  })

  // 5. participants: broker owner (active) + dispatcher contact (invited)
  await admin.from('trip_participants').insert({
    trip_id: trip.id,
    user_id: page.owner_id,
    email: owner?.email ?? '',
    name: owner?.full_name ?? page.display_name,
    role: 'broker',
    status: 'active',
  })
  const dispatcherRow = {
    trip_id: trip.id,
    user_id: null,
    email: input.contact_email,
    name: input.contact_name,
    phone: input.contact_phone,
    role: 'dispatcher',
    status: 'invited',
  }
  let { data: dispatcherParticipant } = await admin
    .from('trip_participants')
    .insert({ ...dispatcherRow, phone_ext: input.contact_phone_ext || null })
    .select()
    .single()
  if (!dispatcherParticipant) {
    // Graceful until migration 0007 adds phone_ext: never lose the dispatcher.
    const retry = await admin.from('trip_participants').insert(dispatcherRow).select().single()
    dispatcherParticipant = retry.data
  }

  // 6. invitation token for the dispatcher
  const { data: invitation } = await admin
    .from('trip_invitations')
    .insert({
      trip_id: trip.id,
      participant_id: dispatcherParticipant?.id ?? null,
      email: input.contact_email,
      role: 'dispatcher',
    })
    .select()
    .single()

  // 7. store the rate confirmation
  const storeFile = async (file: File, kind: string) => {
    const path = `${trip.id}/${crypto.randomUUID()}-${file.name.replace(/[^\w.\-]/g, '_')}`
    const { error: upErr } = await admin.storage
      .from('trip-documents')
      .upload(path, file, { contentType: file.type })
    if (upErr) return null
    const { data: doc } = await admin
      .from('documents')
      .insert({
        trip_id: trip.id,
        kind,
        storage_path: path,
        file_name: file.name,
        mime_type: file.type,
        size_bytes: file.size,
        uploader_label: `${input.contact_name} (intake)`,
      })
      .select()
      .single()
    return doc
  }

  await storeFile(rateCon, 'rate_confirmation')

  // 8. permit path
  if (permitSource === 'upload') {
    for (const file of permitFiles) {
      const doc = await storeFile(file, 'permit')
      if (!doc) continue
      const ex = await extractPermit(file, file.name)
      const p = ex.ok ? ex.data! : undefined
      const { data: permit } = await admin
        .from('permits')
        .insert({
          trip_id: trip.id,
          document_id: doc.id,
          state_code: p?.state_code ?? '',
          permit_number: p?.permit_number ?? null,
          effective_date: p?.effective_date ?? null,
          expiration_date: p?.expiration_date ?? null,
          permit_length_in: p?.length_in ?? null,
          permit_width_in: p?.width_in ?? null,
          permit_height_in: p?.height_in ?? null,
          permit_weight_lbs: p?.weight_lbs ?? null,
          extraction: p ? { ...p } : null,
          extraction_status: ex.ok ? 'processed' : 'pending',
        })
        .select()
        .single()

      if (permit && ex.ok) {
        const warnings = computePermitWarnings(trip, permit)
        if (warnings.length > 0) {
          await admin.from('warnings').insert(
            warnings.map((w) => ({
              trip_id: trip.id,
              permit_id: w.permitId ?? null,
              kind: w.kind,
              severity: w.severity,
              message: w.message,
            })),
          )
        }
      }
    }
    // first processed permit activates the trip
    const { count } = await admin
      .from('permits')
      .select('id', { count: 'exact', head: true })
      .eq('trip_id', trip.id)
      .eq('extraction_status', 'processed')
    if ((count ?? 0) > 0) {
      await admin.from('trips').update({ status: 'active' }).eq('id', trip.id)
    }
  } else {
    // Synchron purchase path: record a manual permit request, notify via adapter
    const { data: permitRequest } = await admin
      .from('service_requests')
      .insert({
        trip_id: trip.id,
        type: 'permit_request',
        notes: input.notes || null,
        requester_label: `${input.contact_name} (intake)`,
      })
      .select('id')
      .single()
    const order = await createSynchronOrder({
      type: 'permit',
      trip_ref: trip.ref_code,
      carrier_name: input.carrier_company,
      contact_email: input.contact_email,
      notes: input.notes || null,
    })
    // Task 84: persist Synchron's order number when it comes back (column
    // from migration 0013; silently skipped before it is applied).
    const vendorOrderId = order.ok ? (order.data?.order_id ?? null) : null
    if (vendorOrderId && permitRequest?.id) {
      const { error: idError } = await admin
        .from('service_requests')
        .update({ vendor_order_id: vendorOrderId })
        .eq('id', permitRequest.id)
      if (idError && !isMissingColumn(idError, 'vendor_order_id')) {
        console.error('vendor_order_id not saved', idError.message)
      }
    }
  }

  // 9. audit
  await admin.from('trip_events').insert([
    {
      trip_id: trip.id,
      actor_label: `${input.contact_name} (public intake)`,
      action: 'trip_created_via_intake',
      detail: { broker_page: page.slug, permit_source: permitSource, source_ip: ip },
    },
    {
      trip_id: trip.id,
      actor_label: 'system',
      action: 'dispatcher_invited',
      detail: { email: input.contact_email },
    },
  ])

  const origin = publicOrigin(req)
  return NextResponse.json({
    ok: true,
    trip_ref: trip.ref_code,
    invite_url: invitation ? `${origin}/invite/${invitation.token}` : null,
  })
}
