'use client'

/**
 * Form entry detail pane — inline InspectorPane replacement for the old
 * `EntryDetailDrawer` overlay. 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. Rendered beside the entries table (400px split pane).
 */
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 {
  InspectorPaneBody,
  InspectorPaneFooter,
  InspectorPaneHeader,
  InspectorPaneSection,
} from '../ui/InspectorPane.js'
import { FormsErrorState } from './primitives.js'

const iconBtn =
  'flex h-7 w-7 shrink-0 items-center justify-center rounded-[6px] text-[var(--muted)] transition-colors duration-[var(--motion-fast)] hover:bg-[var(--bg)] hover:text-[var(--txt)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none disabled:opacity-60'

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 FormEntryDetailPaneProps {
  entryId: string
  onClose: () => 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 FormEntryDetailPane({
  entryId,
  onClose,
  onChanged,
  onDeleted,
}: FormEntryDetailPaneProps) {
  const [detail, setDetail] = useState<EntryDetail | null>(null)
  const [loading, setLoading] = useState(true)
  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 pane opens, marking it read.
  useEffect(() => {
    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
    }
  }, [entryId, notifyChanged])

  const mutate = useCallback(
    async (patch: { unread?: boolean; starred?: boolean; archived?: boolean }) => {
      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 () => {
    setBusy(true)
    const res = await deleteEntry(entryId)
    setBusy(false)
    if (res.error) {
      toast.error(res.error)
      return
    }
    toast.success('Submission deleted')
    notifyChanged()
    onDeleted?.(entryId)
  }, [entryId, notifyChanged, onDeleted])

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

  return (
    <>
      <InspectorPaneHeader onClose={onClose} closeLabel="Close entry details">
        <div className="flex items-start justify-between gap-2">
          <div className="min-w-0">
            <div className="truncate text-[13.5px] font-semibold text-[var(--txt)]">
              {entry ? `Entry${entry.entryNumber ? ` #${entry.entryNumber}` : ''}` : 'Entry'}
            </div>
            {entry && (
              <div className="mt-0.5 truncate text-[11.5px] text-[var(--muted)]">
                {formatDateTime(entry.submittedAt)}
              </div>
            )}
          </div>
          {entry && (
            <div className="flex shrink-0 gap-1">
              <button
                type="button"
                className={iconBtn}
                aria-pressed={entry.starred}
                aria-label={entry.starred ? 'Unstar entry' : 'Star entry'}
                disabled={busy}
                onClick={() => void mutate({ starred: !entry.starred })}
              >
                <Star
                  size={16}
                  aria-hidden
                  className={entry.starred ? 'fill-[var(--warn)] text-[var(--warn)]' : ''}
                />
              </button>
              <button
                type="button"
                className={iconBtn}
                aria-pressed={entry.archived}
                aria-label={entry.archived ? 'Unarchive entry' : 'Archive entry'}
                disabled={busy}
                onClick={() => void mutate({ archived: !entry.archived })}
              >
                {entry.archived ? (
                  <ArchiveRestore size={16} aria-hidden />
                ) : (
                  <Archive size={16} aria-hidden />
                )}
              </button>
            </div>
          )}
        </div>
      </InspectorPaneHeader>

      <InspectorPaneBody>
        {loading ? (
          <div className="flex h-40 items-center justify-center" role="status" aria-live="polite">
            <Loader2 className="h-5 w-5 animate-spin text-[var(--muted)]" aria-hidden />
            <span className="sr-only">Loading entry…</span>
          </div>
        ) : error ? (
          <div className="px-4 py-3.5">
            <FormsErrorState message={error} />
          </div>
        ) : entry ? (
          <>
            <InspectorPaneSection title="Sender">
              <div className="text-[12.5px] font-medium text-[var(--txt)]">{senderName}</div>
              {entry.senderEmail && (
                <a
                  href={`mailto:${entry.senderEmail}`}
                  className="block truncate text-[12px] text-[var(--acc)] hover:underline"
                >
                  {entry.senderEmail}
                </a>
              )}
              {entry.senderPhone && (
                <div className="text-[12px] text-[var(--sub)]">{entry.senderPhone}</div>
              )}
            </InspectorPaneSection>

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

            {attrRows.length > 0 && (
              <InspectorPaneSection title="Attribution">
                <dl>
                  {attrRows.map((row) => (
                    <div key={row.label} className="flex justify-between gap-4 py-1 text-[12px]">
                      <dt className="shrink-0 text-[var(--muted)]">{row.label}</dt>
                      <dd className="min-w-0 truncate text-right text-[var(--sub)]">{row.value}</dd>
                    </div>
                  ))}
                </dl>
              </InspectorPaneSection>
            )}
          </>
        ) : null}
      </InspectorPaneBody>

      {entry && (
        <InspectorPaneFooter>
          <div className="flex items-center gap-2">
            <button
              type="button"
              className="flex min-w-0 flex-1 items-center justify-center gap-1.5 rounded-[7px] border border-[var(--bdr)] px-3 py-2 text-[12.5px] font-medium text-[var(--txt)] transition-colors duration-[var(--motion-fast)] hover:bg-[var(--bg)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none disabled:opacity-60"
              disabled={busy}
              onClick={() => void mutate({ unread: !entry.unread })}
            >
              <Mail size={14} aria-hidden />
              Mark {entry.unread ? 'read' : 'unread'}
            </button>
            <button
              type="button"
              className="flex shrink-0 items-center justify-center gap-1.5 rounded-[7px] border border-[var(--bdr)] px-3 py-2 text-[12.5px] font-medium text-[var(--err)] transition-colors duration-[var(--motion-fast)] hover:bg-[var(--err-l)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none disabled:opacity-60"
              disabled={busy}
              onClick={() => setConfirmDelete(true)}
            >
              <Trash2 size={14} aria-hidden />
              Delete
            </button>
          </div>
        </InspectorPaneFooter>
      )}

      <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 }))

  return (
    <InspectorPaneSection title="Submission">
      {rows.length === 0 ? (
        <p className="text-[12px] text-[var(--muted)]">No field data captured.</p>
      ) : (
        <dl>
          {rows.map((row) => (
            <div key={row.key} className="mb-2.5 last:mb-0">
              <dt className="text-[10.5px] font-semibold tracking-[0.5px] text-[var(--muted)] uppercase">
                {row.label}
              </dt>
              <dd className="mt-0.5 text-[12.5px] break-words whitespace-pre-wrap text-[var(--txt)]">
                {displayValue(entry.data?.[row.key])}
              </dd>
            </div>
          ))}
        </dl>
      )}
    </InspectorPaneSection>
  )
}
