'use client';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
 * All Entries — a cross-form submissions inbox. Same token-only table, search,
 * status pills (All / Unread / Starred / Archived), bulk actions, CSV export,
 * pagination, and inline detail pane 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, } from '../lib/forms-service.js';
import { emitFormsChanged, onFormsChanged } from '../lib/forms-events.js';
import { FormEntryDetailPane } from '../components/forms/FormEntryDetailPane.js';
import { EntriesTable } from '../components/forms/EntriesTable.js';
import { ConfirmDialog } from '../components/ui/ConfirmDialog.js';
import { InspectorPane, SplitPaneLayout, SplitPaneList } from '../components/ui/InspectorPane.js';
import { FormsEmptyState, FormsErrorState, FormsLoading, FormsPagination, SummaryChip, btnSecondary, } from '../components/forms/primitives.js';
const FILTERS = [
    { id: 'all', label: 'All' },
    { id: 'unread', label: 'Unread' },
    { id: 'starred', label: 'Starred' },
    { id: 'archived', label: 'Archived' },
];
const PAGE_SIZE = 25;
function filterToParams(filter) {
    switch (filter) {
        case 'unread':
            return { unread: true };
        case 'starred':
            return { starred: true };
        case 'archived':
            return { archived: true };
        default:
            return {};
    }
}
function csvEscape(value) {
    const s = value == null ? '' : typeof value === 'object' ? JSON.stringify(value) : String(value);
    return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
}
function exportCsv(entries) {
    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 }) {
    const [entries, setEntries] = useState([]);
    const [total, setTotal] = useState(0);
    const [counts, setCounts] = useState(null);
    const [forms, setForms] = useState([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);
    const [search, setSearch] = useState('');
    const [filter, setFilter] = useState('all');
    const [formFilter, setFormFilter] = useState('all');
    const [page, setPage] = useState(1);
    const [selected, setSelected] = useState(new Set());
    const [activeEntry, setActiveEntry] = useState(null);
    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) => {
        // Toggle: clicking the open row again collapses the pane.
        setActiveEntry((cur) => (cur === id ? null : id));
        setEntries((cur) => cur.map((e) => (e.id === id ? { ...e, unread: false } : e)));
    }, []);
    const toggleStar = useCallback(async (entry) => {
        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) => {
        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, label) => {
        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 (_jsxs("div", { className: "space-y-5 p-6", children: [_jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-foreground", children: "All Entries" }), _jsxs("p", { className: "text-muted-foreground mt-1 text-sm", children: [total, " ", total === 1 ? 'submission' : 'submissions', " across your forms"] })] }), _jsxs("button", { type: "button", className: btnSecondary, onClick: () => exportCsv(entries), disabled: entries.length === 0, children: [_jsx(Download, { className: "h-4 w-4", "aria-hidden": true }), "Export CSV"] })] }), counts && (_jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(SummaryChip, { value: counts.total, label: "total" }), _jsx(SummaryChip, { value: counts.unread, label: "unread", tone: counts.unread > 0 ? 'destructive' : 'neutral' }), _jsx(SummaryChip, { value: counts.starred, label: "starred", tone: "warning" }), _jsx(SummaryChip, { value: counts.thisWeek, label: "this week" })] })), _jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "flex flex-1 flex-col gap-3 sm:flex-row sm:items-center", children: [_jsxs("div", { className: "relative w-full sm:max-w-xs", children: [_jsx(Search, { className: "text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2", "aria-hidden": true }), _jsx("input", { type: "search", placeholder: "Search entries\u2026", 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" })] }), _jsxs("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", children: [_jsx("option", { value: "all", children: "All forms" }), forms.map((f) => (_jsx("option", { value: f.id, children: f.name }, f.id)))] })] }), _jsx("div", { className: "flex gap-1", role: "group", "aria-label": "Filter entries", children: FILTERS.map((f) => (_jsx("button", { 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'}`, children: f.label }, f.id))) })] }), selected.size > 0 && (_jsxs("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", children: [_jsxs("span", { className: "text-foreground font-medium", children: [selected.size, " selected"] }), _jsx("span", { className: "bg-border mx-1 h-4 w-px", "aria-hidden": true }), _jsx("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'), children: "Mark read" }), _jsx("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'), children: "Star" }), _jsx("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'), children: filter === 'archived' ? 'Unarchive' : 'Archive' }), _jsx("button", { type: "button", className: "text-destructive hover:bg-destructive/10 rounded-md px-2 py-1 font-medium transition-colors", onClick: () => setConfirmBulkDelete(true), children: "Delete" }), _jsx("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()), children: "Clear" })] })), _jsxs(SplitPaneLayout, { className: "h-[calc(100vh-340px)] min-h-[480px]", children: [_jsx(SplitPaneList, { children: error ? (_jsx(FormsErrorState, { message: error, onRetry: load })) : loading ? (_jsx(FormsLoading, { label: "Loading entries\u2026" })) : showEmpty ? (_jsx(FormsEmptyState, { icon: _jsx(Inbox, { className: "h-6 w-6", "aria-hidden": true }), 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 ? (_jsx("button", { type: "button", onClick: () => {
                                    setSearch('');
                                    setFilter('all');
                                    setFormFilter('all');
                                    setPage(1);
                                }, className: "text-primary text-sm font-medium hover:underline", children: "Clear filters" })) : undefined })) : (_jsx(EntriesTable, { entries: entries, selected: selected, allSelected: allSelected, onToggleSelectAll: toggleSelectAll, onToggleSelect: toggleSelect, onOpen: openEntry, onToggleStar: toggleStar, activeId: activeEntry, showForm: true, onOpenForm: (id) => onNavigate?.(`/forms/${id}/submissions`), ariaLabel: "All entries", footer: _jsx(FormsPagination, { page: page, pageSize: PAGE_SIZE, total: total, onPageChange: setPage, noun: "submission", label: "Entries pagination" }) })) }), _jsx(InspectorPane, { open: activeEntry !== null, width: 400, "aria-label": "Entry details", children: activeEntry && (_jsx(FormEntryDetailPane, { entryId: activeEntry, onClose: () => setActiveEntry(null), onChanged: refresh, onDeleted: () => {
                                setActiveEntry(null);
                                refresh();
                            } }, activeEntry)) })] }), _jsx(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: true })] }));
}
//# sourceMappingURL=FormEntries.js.map