'use client'

/**
 * All Entries — a cross-form submissions inbox. Same token-only table, search,
 * status pills (All / Unread / Starred / Archived), bulk actions, CSV export,
 * pagination, and detail drawer as the per-form Submissions view, plus a Form
 * filter and a Form column so editors can triage every form from one place.
 */
import { useCallback, useEffect, useState } from 'react'
import { Download, Inbox, Search } from 'lucide-react'
import { toast } from 'sonner'
import {
  bulkUpdateEntries,
  deleteEntry,
  fetchEntries,
  fetchEntryCounts,
  fetchForms,
  updateEntry,
  type EntryCounts,
  type FetchFormEntriesParams,
  type FormListItem,
  type FormSubmission,
} from '../lib/forms-service.js'
import { emitFormsChanged, onFormsChanged } from '../lib/forms-events.js'
import { EntryDetailDrawer } from '../components/forms/EntryDetailDrawer.js'
import { EntriesTable } from '../components/forms/EntriesTable.js'
import { ConfirmDialog } from '../components/ui/ConfirmDialog.js'
import {
  FormsEmptyState,
  FormsErrorState,
  FormsLoading,
  FormsPagination,
  SummaryChip,
  btnSecondary,
} from '../components/forms/primitives.js'

export interface FormEntriesProps {
  onNavigate?: (path: string) => void
}

type StatusFilter = 'all' | 'unread' | 'starred' | 'archived'

const FILTERS: { id: StatusFilter; label: string }[] = [
  { id: 'all', label: 'All' },
  { id: 'unread', label: 'Unread' },
  { id: 'starred', label: 'Starred' },
  { id: 'archived', label: 'Archived' },
]

const PAGE_SIZE = 25

function filterToParams(filter: StatusFilter): Partial<FetchFormEntriesParams> {
  switch (filter) {
    case 'unread':
      return { unread: true }
    case 'starred':
      return { starred: true }
    case 'archived':
      return { archived: true }
    default:
      return {}
  }
}

function csvEscape(value: unknown): string {
  const s = value == null ? '' : typeof value === 'object' ? JSON.stringify(value) : String(value)
  return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s
}

function exportCsv(entries: FormSubmission[]) {
  const dataKeys = [...new Set(entries.flatMap((e) => Object.keys(e.data ?? {})))]
  const header = ['Entry', 'Form', 'Submitted', 'Name', 'Email', 'Phone', 'Status', ...dataKeys]
  const lines = [header.map(csvEscape).join(',')]
  for (const e of entries) {
    const row = [
      e.entryNumber ?? '',
      e.formName ?? '',
      e.submittedAt,
      e.senderName ?? '',
      e.senderEmail ?? '',
      e.senderPhone ?? '',
      e.unread ? 'unread' : 'read',
      ...dataKeys.map((k) => e.data?.[k]),
    ]
    lines.push(row.map(csvEscape).join(','))
  }
  const blob = new Blob([lines.join('\n')], { type: 'text/csv;charset=utf-8' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = 'all-entries.csv'
  a.click()
  URL.revokeObjectURL(url)
}

export function FormEntries({ onNavigate }: FormEntriesProps) {
  const [entries, setEntries] = useState<FormSubmission[]>([])
  const [total, setTotal] = useState(0)
  const [counts, setCounts] = useState<EntryCounts | null>(null)
  const [forms, setForms] = useState<FormListItem[]>([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [search, setSearch] = useState('')
  const [filter, setFilter] = useState<StatusFilter>('all')
  const [formFilter, setFormFilter] = useState<string>('all')
  const [page, setPage] = useState(1)
  const [selected, setSelected] = useState<Set<string>>(new Set())
  const [activeEntry, setActiveEntry] = useState<string | null>(null)
  const [drawerOpen, setDrawerOpen] = useState(false)
  const [confirmBulkDelete, setConfirmBulkDelete] = useState(false)

  const scopedFormId = formFilter === 'all' ? undefined : formFilter

  // Forms for the filter dropdown (loads once).
  useEffect(() => {
    let cancelled = false
    void fetchForms()
      .then((list) => {
        if (!cancelled) setForms(list)
      })
      .catch(() => {})
    return () => {
      cancelled = true
    }
  }, [])

  const loadCounts = useCallback(() => {
    void fetchEntryCounts({ formId: scopedFormId })
      .then(setCounts)
      .catch(() => {})
  }, [scopedFormId])

  const load = useCallback(async () => {
    setLoading(true)
    setError(null)
    try {
      const result = await fetchEntries({
        formId: scopedFormId,
        search: search.trim() || undefined,
        page,
        pageSize: PAGE_SIZE,
        ...filterToParams(filter),
      })
      setEntries(result.entries)
      setTotal(result.total)
      setSelected(new Set())
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Failed to load entries')
    } finally {
      setLoading(false)
    }
  }, [scopedFormId, search, filter, page])

  useEffect(() => {
    void load()
  }, [load])

  useEffect(() => {
    loadCounts()
  }, [loadCounts])

  useEffect(() => onFormsChanged(() => loadCounts()), [loadCounts])

  const refresh = useCallback(() => {
    void load()
    loadCounts()
  }, [load, loadCounts])

  const openEntry = useCallback((id: string) => {
    setActiveEntry(id)
    setDrawerOpen(true)
    setEntries((cur) => cur.map((e) => (e.id === id ? { ...e, unread: false } : e)))
  }, [])

  const toggleStar = useCallback(async (entry: FormSubmission) => {
    const next = !entry.starred
    setEntries((cur) => cur.map((e) => (e.id === entry.id ? { ...e, starred: next } : e)))
    const res = await updateEntry(entry.id, { starred: next })
    if (res.error) {
      setEntries((cur) => cur.map((e) => (e.id === entry.id ? { ...e, starred: !next } : e)))
      toast.error(res.error)
      return
    }
    emitFormsChanged()
  }, [])

  const allSelected = entries.length > 0 && selected.size === entries.length
  const toggleSelectAll = useCallback(() => {
    setSelected((cur) =>
      cur.size === entries.length ? new Set() : new Set(entries.map((e) => e.id)),
    )
  }, [entries])

  const toggleSelect = useCallback((id: string) => {
    setSelected((cur) => {
      const next = new Set(cur)
      if (next.has(id)) next.delete(id)
      else next.add(id)
      return next
    })
  }, [])

  const bulkPatch = useCallback(
    async (patch: { unread?: boolean; starred?: boolean; archived?: boolean }, label: string) => {
      const ids = [...selected]
      if (ids.length === 0) return
      const res = await bulkUpdateEntries(ids, patch)
      if (res.error) {
        toast.error(res.error)
        return
      }
      toast.success(`${ids.length} ${ids.length === 1 ? 'entry' : 'entries'} ${label}`)
      emitFormsChanged()
      refresh()
    },
    [selected, refresh],
  )

  const handleBulkDelete = useCallback(async () => {
    const ids = [...selected]
    if (ids.length === 0) return
    const results = await Promise.allSettled(ids.map((id) => deleteEntry(id)))
    const failed = results.filter((r) => r.status === 'rejected' || r.value.error).length
    if (failed > 0) toast.error(`${failed} of ${ids.length} could not be deleted`)
    else toast.success(`${ids.length} ${ids.length === 1 ? 'entry' : 'entries'} deleted`)
    emitFormsChanged()
    refresh()
  }, [selected, refresh])

  const showEmpty = !loading && !error && entries.length === 0
  const isFiltered = filter !== 'all' || formFilter !== 'all' || search.trim().length > 0

  return (
    <div className="space-y-5 p-6">
      <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
        <div>
          <h1 className="text-foreground text-2xl font-medium">All Entries</h1>
          <p className="text-muted-foreground mt-1 text-sm">
            {total} {total === 1 ? 'submission' : 'submissions'} across your forms
          </p>
        </div>
        <button
          type="button"
          className={btnSecondary}
          onClick={() => exportCsv(entries)}
          disabled={entries.length === 0}
        >
          <Download className="h-4 w-4" aria-hidden />
          Export CSV
        </button>
      </div>

      {counts && (
        <div className="flex flex-wrap gap-2">
          <SummaryChip value={counts.total} label="total" />
          <SummaryChip
            value={counts.unread}
            label="unread"
            tone={counts.unread > 0 ? 'destructive' : 'neutral'}
          />
          <SummaryChip value={counts.starred} label="starred" tone="warning" />
          <SummaryChip value={counts.thisWeek} label="this week" />
        </div>
      )}

      <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
        <div className="flex flex-1 flex-col gap-3 sm:flex-row sm:items-center">
          <div className="relative w-full sm:max-w-xs">
            <Search
              className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2"
              aria-hidden
            />
            <input
              type="search"
              placeholder="Search entries…"
              value={search}
              onChange={(e) => {
                setSearch(e.target.value)
                setPage(1)
              }}
              aria-label="Search entries"
              className="border-border bg-input-background text-foreground focus-visible:ring-ring w-full rounded-lg border py-2 pr-3 pl-9 text-sm focus-visible:ring-2 focus-visible:outline-none"
            />
          </div>
          <select
            value={formFilter}
            onChange={(e) => {
              setFormFilter(e.target.value)
              setPage(1)
            }}
            aria-label="Filter by form"
            className="border-border bg-input-background text-foreground focus-visible:ring-ring rounded-lg border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:outline-none"
          >
            <option value="all">All forms</option>
            {forms.map((f) => (
              <option key={f.id} value={f.id}>
                {f.name}
              </option>
            ))}
          </select>
        </div>
        <div className="flex gap-1" role="group" aria-label="Filter entries">
          {FILTERS.map((f) => (
            <button
              key={f.id}
              type="button"
              onClick={() => {
                setFilter(f.id)
                setPage(1)
              }}
              aria-pressed={filter === f.id}
              className={`rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${
                filter === f.id
                  ? 'bg-primary text-primary-foreground'
                  : 'text-muted-foreground hover:bg-accent hover:text-foreground'
              }`}
            >
              {f.label}
            </button>
          ))}
        </div>
      </div>

      {selected.size > 0 && (
        <div
          className="border-border bg-accent/40 flex flex-wrap items-center gap-2 rounded-lg border px-3 py-2 text-sm"
          role="region"
          aria-label="Bulk actions"
        >
          <span className="text-foreground font-medium">{selected.size} selected</span>
          <span className="bg-border mx-1 h-4 w-px" aria-hidden />
          <button
            type="button"
            className="text-foreground hover:bg-accent rounded-md px-2 py-1 font-medium transition-colors"
            onClick={() => void bulkPatch({ unread: false }, 'marked read')}
          >
            Mark read
          </button>
          <button
            type="button"
            className="text-foreground hover:bg-accent rounded-md px-2 py-1 font-medium transition-colors"
            onClick={() => void bulkPatch({ starred: true }, 'starred')}
          >
            Star
          </button>
          <button
            type="button"
            className="text-foreground hover:bg-accent rounded-md px-2 py-1 font-medium transition-colors"
            onClick={() =>
              void bulkPatch(
                { archived: filter !== 'archived' },
                filter !== 'archived' ? 'archived' : 'unarchived',
              )
            }
          >
            {filter === 'archived' ? 'Unarchive' : 'Archive'}
          </button>
          <button
            type="button"
            className="text-destructive hover:bg-destructive/10 rounded-md px-2 py-1 font-medium transition-colors"
            onClick={() => setConfirmBulkDelete(true)}
          >
            Delete
          </button>
          <button
            type="button"
            className="text-muted-foreground hover:text-foreground ml-auto rounded-md px-2 py-1 font-medium transition-colors"
            onClick={() => setSelected(new Set())}
          >
            Clear
          </button>
        </div>
      )}

      {error ? (
        <FormsErrorState message={error} onRetry={load} />
      ) : loading ? (
        <FormsLoading label="Loading entries…" />
      ) : showEmpty ? (
        <FormsEmptyState
          icon={<Inbox className="h-6 w-6" aria-hidden />}
          title={isFiltered ? 'No entries match your filters' : 'No entries yet'}
          description={
            isFiltered
              ? 'Try a different search term, form, or status filter.'
              : 'Submissions across all your forms will appear here.'
          }
          action={
            isFiltered ? (
              <button
                type="button"
                onClick={() => {
                  setSearch('')
                  setFilter('all')
                  setFormFilter('all')
                  setPage(1)
                }}
                className="text-primary text-sm font-medium hover:underline"
              >
                Clear filters
              </button>
            ) : undefined
          }
        />
      ) : (
        <EntriesTable
          entries={entries}
          selected={selected}
          allSelected={allSelected}
          onToggleSelectAll={toggleSelectAll}
          onToggleSelect={toggleSelect}
          onOpen={openEntry}
          onToggleStar={toggleStar}
          showForm
          onOpenForm={(id) => onNavigate?.(`/forms/${id}/submissions`)}
          ariaLabel="All entries"
          footer={
            <FormsPagination
              page={page}
              pageSize={PAGE_SIZE}
              total={total}
              onPageChange={setPage}
              noun="submission"
              label="Entries pagination"
            />
          }
        />
      )}

      <EntryDetailDrawer
        entryId={activeEntry}
        open={drawerOpen}
        onOpenChange={setDrawerOpen}
        onChanged={refresh}
        onDeleted={() => refresh()}
      />

      <ConfirmDialog
        open={confirmBulkDelete}
        onClose={() => setConfirmBulkDelete(false)}
        onConfirm={() => void handleBulkDelete()}
        title={`Delete ${selected.size} ${selected.size === 1 ? 'submission' : 'submissions'}?`}
        description="This permanently removes the selected submissions and any attached files. This cannot be undone."
        confirmLabel="Delete"
        destructive
      />
    </div>
  )
}
