'use client'

/**
 * Entry detail drawer — shows a single submission rendered against the schema
 * version it was captured with, plus sender, attribution, and per-entry
 * actions (star, archive, mark unread, delete). Opening an unread entry marks
 * it read. Token-only styling; built on the shared `Drawer`.
 */
import { useCallback, useEffect, useState } from 'react'
import { Archive, ArchiveRestore, Loader2, Mail, Star, Trash2 } from 'lucide-react'
import { toast } from 'sonner'
import {
  deleteEntry,
  fetchEntry,
  updateEntry,
  type EntryDetail,
  type FormField,
  type FormSubmission,
} from '../../lib/forms-service.js'
import { emitFormsChanged } from '../../lib/forms-events.js'
import { ConfirmDialog } from '../ui/ConfirmDialog.js'
import { Drawer } from './Drawer.js'
import { FormsErrorState, btnGhostIcon } from './primitives.js'

function formatDateTime(iso: string | null | undefined): string {
  if (!iso) return '—'
  const d = new Date(iso)
  if (Number.isNaN(d.getTime())) return '—'
  return d.toLocaleString(undefined, {
    month: 'short',
    day: 'numeric',
    year: 'numeric',
    hour: 'numeric',
    minute: '2-digit',
  })
}

/** Render a stored value for display, coping with arrays/objects/booleans. */
function displayValue(value: unknown): string {
  if (value == null || value === '') return '—'
  if (typeof value === 'boolean') return value ? 'Yes' : 'No'
  if (Array.isArray(value)) return value.map((v) => String(v)).join(', ')
  if (typeof value === 'object') return JSON.stringify(value)
  return String(value)
}

interface AttrRow {
  label: string
  value: string | null | undefined
}

function attributionRows(entry: FormSubmission): AttrRow[] {
  return [
    { label: 'Source', value: entry.utmSource },
    { label: 'Medium', value: entry.utmMedium },
    { label: 'Campaign', value: entry.utmCampaign },
    { label: 'Page', value: entry.pageUrl },
    { label: 'Referrer', value: entry.referrer },
  ].filter((r) => r.value)
}

export interface EntryDetailDrawerProps {
  entryId: string | null
  open: boolean
  onOpenChange: (open: boolean) => void
  /** Called after a mutation (star/archive/read/delete) so the list refreshes. */
  onChanged?: () => void
  /** Called specifically after a delete so the caller can drop the row/selection. */
  onDeleted?: (id: string) => void
}

export function EntryDetailDrawer({
  entryId,
  open,
  onOpenChange,
  onChanged,
  onDeleted,
}: EntryDetailDrawerProps) {
  const [detail, setDetail] = useState<EntryDetail | null>(null)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const [busy, setBusy] = useState(false)
  const [confirmDelete, setConfirmDelete] = useState(false)

  const notifyChanged = useCallback(() => {
    emitFormsChanged()
    onChanged?.()
  }, [onChanged])

  // Load the entry (and its schema) when the drawer opens, marking it read.
  useEffect(() => {
    if (!open || !entryId) return
    let cancelled = false
    setLoading(true)
    setError(null)
    setDetail(null)
    void (async () => {
      try {
        const data = await fetchEntry(entryId)
        if (cancelled) return
        if (!data) {
          setError('Entry not found')
          return
        }
        setDetail(data)
        // Mark read on open (fire-and-forget; refresh the list counts).
        if (data.entry.unread) {
          const res = await updateEntry(entryId, { unread: false })
          if (!cancelled && !res.error) notifyChanged()
        }
      } catch (e) {
        if (!cancelled) setError(e instanceof Error ? e.message : 'Failed to load entry')
      } finally {
        if (!cancelled) setLoading(false)
      }
    })()
    return () => {
      cancelled = true
    }
  }, [open, entryId, notifyChanged])

  const mutate = useCallback(
    async (patch: { unread?: boolean; starred?: boolean; archived?: boolean }) => {
      if (!entryId) return
      setBusy(true)
      const res = await updateEntry(entryId, patch)
      setBusy(false)
      if (res.error) {
        toast.error(res.error)
        return
      }
      setDetail((cur) => (cur ? { ...cur, entry: { ...cur.entry, ...patch } } : cur))
      notifyChanged()
    },
    [entryId, notifyChanged],
  )

  const handleDelete = useCallback(async () => {
    if (!entryId) return
    setBusy(true)
    const res = await deleteEntry(entryId)
    setBusy(false)
    if (res.error) {
      toast.error(res.error)
      return
    }
    toast.success('Submission deleted')
    notifyChanged()
    onDeleted?.(entryId)
    onOpenChange(false)
  }, [entryId, notifyChanged, onDeleted, onOpenChange])

  const entry = detail?.entry
  const senderName = entry?.senderName || 'Anonymous'
  const attrRows = entry ? attributionRows(entry) : []

  return (
    <>
      <Drawer
        open={open}
        onOpenChange={onOpenChange}
        title={entry ? `Entry${entry.entryNumber ? ` #${entry.entryNumber}` : ''}` : 'Entry'}
        description={entry ? formatDateTime(entry.submittedAt) : undefined}
        headerExtra={
          entry ? (
            <>
              <button
                type="button"
                className={btnGhostIcon}
                aria-pressed={entry.starred}
                aria-label={entry.starred ? 'Unstar entry' : 'Star entry'}
                disabled={busy}
                onClick={() => void mutate({ starred: !entry.starred })}
              >
                <Star
                  size={18}
                  aria-hidden
                  className={entry.starred ? 'fill-warning text-warning' : ''}
                />
              </button>
              <button
                type="button"
                className={btnGhostIcon}
                aria-pressed={entry.archived}
                aria-label={entry.archived ? 'Unarchive entry' : 'Archive entry'}
                disabled={busy}
                onClick={() => void mutate({ archived: !entry.archived })}
              >
                {entry.archived ? (
                  <ArchiveRestore size={18} aria-hidden />
                ) : (
                  <Archive size={18} aria-hidden />
                )}
              </button>
            </>
          ) : undefined
        }
        footer={
          entry ? (
            <>
              <button
                type="button"
                className="text-destructive hover:bg-destructive/10 focus-visible:ring-ring inline-flex items-center gap-2 rounded-md px-3 py-2 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:opacity-60"
                disabled={busy}
                onClick={() => setConfirmDelete(true)}
              >
                <Trash2 size={16} aria-hidden />
                Delete
              </button>
              <button
                type="button"
                className="border-border text-foreground hover:bg-accent focus-visible:ring-ring inline-flex items-center gap-2 rounded-md border px-3 py-2 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:opacity-60"
                disabled={busy}
                onClick={() => void mutate({ unread: !entry.unread })}
              >
                <Mail size={16} aria-hidden />
                Mark {entry.unread ? 'read' : 'unread'}
              </button>
            </>
          ) : undefined
        }
      >
        {loading ? (
          <div className="flex h-40 items-center justify-center" role="status" aria-live="polite">
            <Loader2 className="text-muted-foreground h-5 w-5 animate-spin" aria-hidden />
            <span className="sr-only">Loading entry…</span>
          </div>
        ) : error ? (
          <FormsErrorState message={error} />
        ) : entry ? (
          <div className="space-y-6">
            <section className="space-y-1">
              <h3 className="text-foreground text-sm font-medium">{senderName}</h3>
              {entry.senderEmail && (
                <a
                  href={`mailto:${entry.senderEmail}`}
                  className="text-primary block text-sm hover:underline"
                >
                  {entry.senderEmail}
                </a>
              )}
              {entry.senderPhone && (
                <span className="text-muted-foreground block text-sm">{entry.senderPhone}</span>
              )}
            </section>

            <FieldValues entry={entry} fields={detail.fields} />

            {attrRows.length > 0 && (
              <section>
                <h4 className="text-muted-foreground mb-2 text-xs font-medium tracking-wide uppercase">
                  Attribution
                </h4>
                <dl className="divide-border divide-y text-sm">
                  {attrRows.map((row) => (
                    <div key={row.label} className="flex justify-between gap-4 py-1.5">
                      <dt className="text-muted-foreground shrink-0">{row.label}</dt>
                      <dd className="text-foreground min-w-0 truncate text-right">{row.value}</dd>
                    </div>
                  ))}
                </dl>
              </section>
            )}
          </div>
        ) : null}
      </Drawer>

      <ConfirmDialog
        open={confirmDelete}
        onClose={() => setConfirmDelete(false)}
        onConfirm={() => void handleDelete()}
        title="Delete submission?"
        description="This permanently removes the submission and any attached files. This cannot be undone."
        confirmLabel="Delete"
        destructive
      />
    </>
  )
}

/** Render the captured field values against the entry's schema. */
function FieldValues({ entry, fields }: { entry: FormSubmission; fields: FormField[] }) {
  // Prefer the schema order; fall back to whatever keys exist on the data.
  const inputFields = fields.filter(
    (f) => f.type !== 'divider' && f.type !== 'richtext' && f.type !== 'honeypot',
  )
  const rows: { key: string; label: string }[] = inputFields.length
    ? inputFields.map((f) => ({ key: f.key, label: f.label }))
    : Object.keys(entry.data ?? {}).map((k) => ({ key: k, label: k }))

  if (rows.length === 0) {
    return <p className="text-muted-foreground text-sm">No field data captured.</p>
  }

  return (
    <section>
      <h4 className="text-muted-foreground mb-2 text-xs font-medium tracking-wide uppercase">
        Submission
      </h4>
      <dl className="space-y-3">
        {rows.map((row) => (
          <div key={row.key}>
            <dt className="text-muted-foreground text-xs">{row.label}</dt>
            <dd className="text-foreground mt-0.5 text-sm wrap-break-word whitespace-pre-wrap">
              {displayValue(entry.data?.[row.key])}
            </dd>
          </div>
        ))}
      </dl>
    </section>
  )
}
