'use client'

import { useCallback, useEffect, useRef, useState } from 'react'
import { toast } from 'sonner'
import { Button } from '@/components/ui/button'
import {
  Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger,
} from '@/components/ui/dialog'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Textarea } from '@/components/ui/textarea'
import { STATE_NAMES, stateName } from '@/lib/domain/states'
import type { PaymentReadiness } from '@/lib/domain/purchases'
import type { CardOnFile } from '@/lib/integrations/synchron-payments/types'
import type { PermitRequestDraft, TripDocument, TripParticipant } from '@/types/db'

/**
 * "Buy more permits" (Task 14): order a NEW permit for a state from inside the
 * trip. Supporting documents (route surveys etc.) can be attached, and the
 * requester chooses which OTHER participants get informed — or keeps it
 * private. The email itself (recipient: Synchron Permits, CC: the chosen
 * people, trip data attached) is sent by the backend later; everything is
 * recorded now.
 *
 * 2026-09-29 (client decision D4, Carrier Dispatch only — D6): on the carrier
 * page this is a TWO-STEP dialog. Step 1 is the form above, its button reads
 * "Continue" and saves a draft (files already uploaded). Step 2 asks Synchron
 * for the card on file and hands off to Synchron's secure page for the card
 * choice + CVV; the permit order is only released after that confirms. HHA
 * never renders a card-number or CVV field (doc §20). Other page contexts
 * keep the one-step behaviour unchanged.
 */
export function BuyPermitsDialog({
  tripId,
  pageContext,
  participants,
  documents,
  onDone,
  triggerLabel = 'Buy more permits',
  resumeDraftId,
  resumeSetupRef,
}: {
  tripId: string
  /** trips.ref_code — the CD page Synchron sends the customer back to (the server builds that URL). */
  tripRef: string
  /** Which role page mounts the dialog; the two-step flow is `carrier` only (D6). */
  pageContext: 'broker' | 'carrier'
  participants: TripParticipant[]
  /** The trip's documents, to show the names of files a resumed draft already uploaded. */
  documents?: TripDocument[]
  onDone: () => void
  /** Task 84: Section 2's zero-permit Synchron view opens the same dialog
      under the wording Nash chose ("our existing order permit from trusted partner"). */
  triggerLabel?: string
  /** `?draft=` on the CD trip page: reopen this draft (back from Synchron's card page). */
  resumeDraftId?: string
  /** `?setup=` on the CD trip page: the card session to complete the order with. */
  resumeSetupRef?: string
}) {
  const twoStep = pageContext === 'carrier'
  const [open, setOpen] = useState(false)
  const [pending, setPending] = useState(false)
  const [files, setFiles] = useState<File[]>([])
  const [notify, setNotify] = useState<Set<string>>(new Set())
  // Nash: pick states "as you have it on hashtags" — type, suggest, add a chip.
  const [pickedStates, setPickedStates] = useState<string[]>([])
  const [stateQuery, setStateQuery] = useState('')

  // Two-step state (carrier). The draft survives the redirect to Synchron.
  const [step, setStep] = useState<1 | 2>(1)
  const [draft, setDraft] = useState<PermitRequestDraft | null>(null)
  const [notesDraft, setNotesDraft] = useState('')
  const [readiness, setReadiness] = useState<PaymentReadiness>('checking')
  const [cards, setCards] = useState<CardOnFile[]>([])
  const [cardId, setCardId] = useState<string | null>(null)
  const [resumedFrom, setResumedFrom] = useState<string | null>(null)
  const resumeHandled = useRef(false)

  const suggestions =
    stateQuery.trim() === ''
      ? []
      : Object.entries(STATE_NAMES)
          .filter(
            ([code, name]) =>
              !pickedStates.includes(code) &&
              (name.toLowerCase().startsWith(stateQuery.trim().toLowerCase()) ||
                code.toLowerCase().startsWith(stateQuery.trim().toLowerCase())),
          )
          .slice(0, 6)

  function pickState(code: string) {
    setPickedStates((prev) => (prev.includes(code) ? prev : [...prev, code]))
    setStateQuery('')
  }

  function resetForm() {
    setFiles([])
    setNotify(new Set())
    setPickedStates([])
    setStateQuery('')
    setStep(1)
    setDraft(null)
    setNotesDraft('')
    setReadiness('checking')
    setCards([])
    setCardId(null)
  }

  function finish(states: string[]) {
    toast.success(
      `Permit order${states.length === 1 ? '' : 's'} for ${states
        .map((s) => stateName(s))
        .join(', ')} sent to our trusted partner Synchron Permits`,
    )
    setOpen(false)
    resetForm()
    onDone()
  }

  /** Step 2 entry: always ask Synchron (never a cached answer). */
  const checkReadiness = useCallback(async () => {
    setReadiness('checking')
    try {
      const res = await fetch(`/api/trips/${tripId}/permit-payment-readiness`, { cache: 'no-store' })
      const json = await res.json().catch(() => ({}))
      if (!res.ok || !json.ok) {
        setReadiness('unavailable')
        setCards([])
        return
      }
      const list: CardOnFile[] = Array.isArray(json.cards) ? json.cards : []
      setCards(list)
      setCardId((prev) => (prev && list.some((c) => c.id === prev) ? prev : (list.find((c) => c.isDefault) ?? list[0])?.id ?? null))
      setReadiness(json.readiness as PaymentReadiness)
    } catch {
      setReadiness('unavailable')
      setCards([])
    }
  }, [tripId])

  useEffect(() => {
    if (open && twoStep && step === 2 && draft && !resumedFrom) queueMicrotask(() => void checkReadiness())
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open, twoStep, step, draft?.id])

  async function completeOrder(d: PermitRequestDraft, setupReference?: string) {
    setPending(true)
    setReadiness('checking')
    try {
      const res = await fetch(`/api/trips/${tripId}/permit-payment/complete`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ draft_id: d.id, ...(setupReference ? { setup_reference: setupReference } : {}) }),
      })
      const json = await res.json().catch(() => ({}))
      if (res.ok && json.ok) {
        finish(d.state_codes)
        return
      }
      setResumedFrom(null)
      if (!res.ok) {
        toast.error(json.error ?? 'Could not send the permit order.')
        await checkReadiness()
        return
      }
      if (json.status === 'pending') setReadiness('setup_pending')
      else if (json.status === 'failed') setReadiness('setup_failed')
      else if (json.status === 'unavailable') setReadiness('unavailable')
      else await checkReadiness()
    } finally {
      setPending(false)
    }
  }

  /** Back from Synchron's card page: reopen the draft and finish the order. */
  useEffect(() => {
    if (!twoStep || !resumeDraftId || resumeHandled.current) return
    resumeHandled.current = true
    let cancelled = false
    ;(async () => {
      const res = await fetch(`/api/trips/${tripId}/permit-request-drafts/${resumeDraftId}`, { cache: 'no-store' })
      const json = await res.json().catch(() => ({}))
      if (cancelled) return
      if (!res.ok || !json.ok) {
        toast.error(json.error ?? 'Could not reopen your permit order.')
        return
      }
      const d = json.draft as PermitRequestDraft
      if (d.status === 'submitted') {
        toast.success('This permit order was already sent to Synchron Permits.')
        return
      }
      setDraft(d)
      setPickedStates(d.state_codes)
      setNotesDraft(d.notes ?? '')
      setNotify(new Set(d.notify ?? []))
      setStep(2)
      setOpen(true)
      setResumedFrom(resumeSetupRef ?? d.setup_reference ?? null)
      if (resumeSetupRef || d.setup_reference) {
        await completeOrder(d, resumeSetupRef ?? d.setup_reference ?? undefined)
      } else {
        setResumedFrom(null)
        await checkReadiness()
      }
    })()
    return () => {
      cancelled = true
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [twoStep, resumeDraftId, resumeSetupRef, tripId])

  async function redirectTo(path: 'card-setup' | 'confirm-card') {
    if (!draft) return
    setPending(true)
    try {
      const res = await fetch(`/api/trips/${tripId}/permit-payment/${path}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ draft_id: draft.id, ...(path === 'confirm-card' ? { card_id: cardId } : {}) }),
      })
      const json = await res.json().catch(() => ({}))
      if (!res.ok || !json.ok || !json.setup_url) {
        toast.error(json.error ?? 'Synchron Permits is unavailable right now. Your request is saved — please try again shortly.')
        setPending(false)
        return
      }
      // Synchron's secure page takes over; it sends the customer back to this trip.
      window.location.assign(json.setup_url as string)
    } catch {
      toast.error('Synchron Permits is unavailable right now. Your request is saved — please try again shortly.')
      setPending(false)
    }
  }

  async function submit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault()
    if (pickedStates.length === 0) {
      toast.error('Add at least one state — start typing and pick from the list.')
      return
    }
    setPending(true)
    try {
      const fd = new FormData(e.currentTarget)

      // Supporting documents land in the trip's Docs first.
      const uploadedNames: string[] = []
      const uploadedIds: string[] = []
      for (const file of files) {
        const upload = new FormData()
        upload.set('file', file)
        upload.set('kind', 'other')
        const res = await fetch(`/api/trips/${tripId}/documents`, { method: 'POST', body: upload })
        if (res.ok) {
          uploadedNames.push(file.name)
          const json = await res.json().catch(() => ({}))
          if (typeof json.document_id === 'string') uploadedIds.push(json.document_id)
        } else toast.error(`Could not upload ${file.name}`)
      }

      const rawNotes = String(fd.get('notes') ?? '').trim()

      if (twoStep) {
        // Step 1 → draft. No request is created until the card step confirms.
        const res = await fetch(`/api/trips/${tripId}/permit-request-drafts`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            state_codes: pickedStates,
            notes: rawNotes,
            document_ids: [...(draft?.document_ids ?? []), ...uploadedIds],
            notify: [...notify],
          }),
        })
        const json = await res.json().catch(() => ({}))
        if (!res.ok || !json.ok) {
          toast.error(json.error ?? 'Could not save your permit order.')
          return
        }
        setDraft(json.draft as PermitRequestDraft)
        setNotesDraft(rawNotes)
        setFiles([])
        setStep(2)
        return
      }

      const notes = [rawNotes, uploadedNames.length > 0 ? `Supporting docs: ${uploadedNames.join(', ')}` : '']
        .filter(Boolean)
        .join('\n')

      // One request per state — each state tracks its own status in the
      // "Requests to Synchron Permits" history.
      let failed = 0
      for (const state of pickedStates) {
        const res = await fetch(`/api/trips/${tripId}/requests`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            type: 'permit_request',
            state_code: state,
            notes,
            notify: [...notify],
          }),
        })
        if (!res.ok) {
          failed++
          const json = await res.json().catch(() => ({}))
          toast.error(json.error ?? `Request for ${stateName(state)} failed`)
        }
      }
      if (failed < pickedStates.length) {
        toast.success(
          `Permit order${pickedStates.length - failed === 1 ? '' : 's'} for ${pickedStates
            .map((s) => stateName(s))
            .join(', ')} sent to our trusted partner Synchron Permits`,
        )
        setOpen(false)
        setFiles([])
        setNotify(new Set())
        setPickedStates([])
        setStateQuery('')
        onDone()
      }
    } finally {
      setPending(false)
    }
  }

  const attachedCount = draft?.document_ids.length ?? 0
  const attachedNames = documents
    ? (draft?.document_ids ?? []).map((id) => documents.find((d) => d.id === id)?.file_name).filter((n): n is string => !!n)
    : []

  return (
    <Dialog
      open={open}
      onOpenChange={(next) => {
        setOpen(next)
        // Closing keeps the draft on the server; the form starts clean next time.
        if (!next && twoStep) resetForm()
      }}
    >
      <DialogTrigger asChild>
        <Button className="w-full bg-[#0f1b2d] font-bold hover:bg-[#1c2f4a]">
          {triggerLabel}
        </Button>
      </DialogTrigger>
      <DialogContent className="max-h-[85vh] overflow-y-auto">
        <DialogHeader>
          <DialogTitle>
            {twoStep && step === 2 ? 'Payment' : 'Order permits from our trusted partner Synchron Permits'}
          </DialogTitle>
        </DialogHeader>
        {twoStep && step === 2 && draft ? (
          <PaymentStep
            draft={draft}
            readiness={readiness}
            cards={cards}
            cardId={cardId}
            pending={pending}
            resuming={!!resumedFrom}
            onSelectCard={setCardId}
            onSend={() => redirectTo('confirm-card')}
            onAddCard={() => redirectTo('card-setup')}
            onRecheck={() => void checkReadiness()}
            onBack={() => setStep(1)}
          />
        ) : (
        <form onSubmit={submit} className="space-y-4">
          <div className="space-y-2">
            <Label htmlFor="state_query">
              States <span className="text-red-600">*</span>
            </Label>
            {pickedStates.length > 0 && (
              <div className="flex flex-wrap gap-1.5">
                {pickedStates.map((code) => (
                  <span
                    key={code}
                    className="flex items-center gap-1 rounded-full bg-[#0f1b2d] px-2.5 py-1 text-xs font-semibold text-white"
                  >
                    {stateName(code)}
                    <button
                      type="button"
                      onClick={() => setPickedStates((prev) => prev.filter((c) => c !== code))}
                      className="ml-0.5 rounded-full px-0.5 leading-none hover:text-[#f5a623]"
                      title={`Remove ${stateName(code)}`}
                    >
                      ×
                    </button>
                  </span>
                ))}
              </div>
            )}
            <div className="relative">
              <Input
                id="state_query"
                value={stateQuery}
                onChange={(e) => setStateQuery(e.target.value)}
                onKeyDown={(e) => {
                  // Tab or Enter fills the top suggestion and readies the next
                  // one — "he can click tab on the keyboard and it fulfills
                  // that name and gives me the next one."
                  if ((e.key === 'Tab' || e.key === 'Enter') && suggestions.length > 0) {
                    e.preventDefault()
                    pickState(suggestions[0][0])
                  }
                }}
                placeholder="Start typing a state — e.g. Ari… or AZ"
                autoComplete="off"
              />
              {suggestions.length > 0 && (
                <div className="absolute left-0 right-0 top-full z-20 mt-1 overflow-hidden rounded-lg border bg-white shadow-lg">
                  {suggestions.map(([code, name], i) => (
                    <button
                      key={code}
                      type="button"
                      onClick={() => pickState(code)}
                      className={`block w-full px-3 py-2 text-left text-sm hover:bg-neutral-50 ${
                        i === 0 ? 'bg-neutral-50 font-semibold' : ''
                      }`}
                    >
                      {name} <span className="text-xs text-neutral-400">{code}</span>
                    </button>
                  ))}
                </div>
              )}
            </div>
            <p className="text-[11px] text-neutral-400">
              Type and pick from the list (or press Tab) — add as many states as you need.
            </p>
          </div>
          <div className="space-y-2">
            <Label htmlFor="req_notes">Special comments</Label>
            <Textarea
              id="req_notes"
              name="notes"
              rows={3}
              placeholder="Anything we should know?"
              {...(twoStep ? { value: notesDraft, onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => setNotesDraft(e.target.value) } : {})}
            />
          </div>
          <div className="space-y-2">
            <Label htmlFor="req_files">Supporting documents (optional)</Label>
            <Input
              id="req_files"
              type="file"
              multiple
              accept=".pdf,.jpg,.jpeg,.png,.docx"
              onChange={(e) => setFiles([...(e.target.files ?? [])])}
            />
            {files.length > 0 && (
              <p className="text-xs text-neutral-500">
                {files.map((f) => f.name).join(' · ')}
              </p>
            )}
            {twoStep && attachedCount > 0 && (
              <p className="text-xs text-neutral-500">
                {attachedNames.length > 0
                  ? `Already attached: ${attachedNames.join(' · ')}`
                  : `${attachedCount} supporting document${attachedCount === 1 ? '' : 's'} attached`}
              </p>
            )}
            <p className="text-[11px] text-neutral-400">
              Route surveys or other files the permit team needs — they attach to this trip.
            </p>
          </div>
          {participants.length > 0 && (
            <div className="space-y-2">
              <Label>Who should be informed?</Label>
              <div className="space-y-1.5">
                {participants.map((pt) => (
                  <label key={pt.id} className="flex cursor-pointer items-center gap-2 text-sm">
                    <input
                      type="checkbox"
                      checked={notify.has(pt.id)}
                      onChange={(e) =>
                        setNotify((prev) => {
                          const next = new Set(prev)
                          if (e.target.checked) next.add(pt.id)
                          else next.delete(pt.id)
                          return next
                        })
                      }
                      className="h-4 w-4 accent-[#0f1b2d]"
                    />
                    <span>
                      {pt.name || pt.email}
                      <span className="ml-1 text-xs capitalize text-neutral-500">· {pt.role}</span>
                    </span>
                  </label>
                ))}
              </div>
              <p className="text-[11px] text-neutral-400">
                Checked people are informed about this order. Leave everyone unchecked to keep it
                between you and Synchron Permits.
              </p>
            </div>
          )}
          <p className="text-xs text-neutral-500">
            The order goes to our trusted partner Synchron Permits with this trip&apos;s details,
            and its status shows on the trip. Payment for permit processing is collected directly
            by Synchron Permits.
          </p>
          <Button type="submit" className="w-full" disabled={pending}>
            {twoStep ? (pending ? 'Saving…' : 'Continue') : pending ? 'Sending…' : 'Send permit order'}
          </Button>
        </form>
        )}
      </DialogContent>
    </Dialog>
  )
}

/** Step 2 — payment readiness per spec §18. Card and CVV live on Synchron's page, never here. */
function PaymentStep({
  draft,
  readiness,
  cards,
  cardId,
  pending,
  resuming,
  onSelectCard,
  onSend,
  onAddCard,
  onRecheck,
  onBack,
}: {
  draft: PermitRequestDraft
  readiness: PaymentReadiness
  cards: CardOnFile[]
  cardId: string | null
  pending: boolean
  resuming: boolean
  onSelectCard: (id: string) => void
  onSend: () => void
  onAddCard: () => void
  onRecheck: () => void
  onBack: () => void
}) {
  const states = draft.state_codes.map((s) => stateName(s)).join(', ')
  return (
    <div className="space-y-4">
      <p className="text-sm text-neutral-600">
        Permit order for <span className="font-semibold text-neutral-900">{states}</span>.
      </p>

      {readiness === 'checking' && (
        <div className="flex items-center gap-2 py-4 text-sm text-neutral-500" role="status">
          <span className="h-4 w-4 animate-spin rounded-full border-2 border-neutral-300 border-t-[#0f1b2d]" aria-hidden />
          {resuming ? 'Finishing your permit order with Synchron Permits…' : 'Checking your payment method with Synchron Permits…'}
        </div>
      )}

      {readiness === 'card_on_file' && (
        <div className="space-y-3">
          <Label>Card on file</Label>
          <div className="space-y-1.5" role="radiogroup" aria-label="Card on file">
            {cards.map((c) => {
              const selected = c.id === cardId
              return (
                <button
                  key={c.id}
                  type="button"
                  role="radio"
                  aria-checked={selected}
                  disabled={!c.available || pending}
                  onClick={() => onSelectCard(c.id)}
                  className={`flex w-full items-center justify-between rounded-lg border px-3 py-2 text-left text-sm disabled:opacity-50 ${
                    selected ? 'border-[#0f1b2d] bg-neutral-50 ring-1 ring-[#0f1b2d]' : 'hover:bg-neutral-50'
                  }`}
                >
                  <span>
                    <span className="font-semibold">{c.brand}</span> •••• {c.last4}
                    <span className="ml-2 text-xs text-neutral-500">
                      exp {String(c.expMonth).padStart(2, '0')}/{String(c.expYear).slice(-2)}
                    </span>
                  </span>
                  {c.isDefault && (
                    <span className="rounded-full bg-neutral-200 px-2 py-0.5 text-[10px] font-semibold uppercase text-neutral-700">
                      default
                    </span>
                  )}
                </button>
              )
            })}
          </div>
          <p className="text-xs text-neutral-500">
            You&apos;ll confirm the card&apos;s security code on Synchron Permits&apos; secure page.
          </p>
          <Button type="button" className="w-full" disabled={pending || !cardId} onClick={onSend}>
            {pending ? 'Opening Synchron Permits…' : 'Send permit order'}
          </Button>
        </div>
      )}

      {readiness === 'card_required' && (
        <div className="space-y-3">
          <p className="text-sm text-neutral-700">
            Add a payment method to your Synchron Permits billing profile to continue.
          </p>
          <Button type="button" className="w-full" disabled={pending} onClick={onAddCard}>
            {pending ? 'Opening Synchron Permits…' : 'Add Card Securely'}
          </Button>
        </div>
      )}

      {readiness === 'setup_pending' && (
        <div className="space-y-3">
          <p className="text-sm text-neutral-700">Waiting for Synchron to confirm your payment method.</p>
          <Button type="button" variant="outline" className="w-full" disabled={pending} onClick={onRecheck}>
            Check again
          </Button>
        </div>
      )}

      {readiness === 'setup_failed' && (
        <div className="space-y-3">
          <p className="text-sm text-red-700">Your payment method could not be confirmed.</p>
          <Button type="button" className="w-full" disabled={pending} onClick={onAddCard}>
            {pending ? 'Opening Synchron Permits…' : 'Try again'}
          </Button>
        </div>
      )}

      {readiness === 'unavailable' && (
        <div className="space-y-3">
          <p className="text-sm text-neutral-700">
            Synchron Permits is unavailable right now. Your request is saved — please try again shortly.
          </p>
          <Button type="button" className="w-full" disabled>
            Send permit order
          </Button>
          <Button type="button" variant="outline" className="w-full" disabled={pending} onClick={onRecheck}>
            Check again
          </Button>
        </div>
      )}

      <button
        type="button"
        onClick={onBack}
        disabled={pending}
        className="text-xs text-neutral-500 underline-offset-2 hover:underline disabled:opacity-50"
      >
        ← Back
      </button>
    </div>
  )
}
