'use client';
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Search, Settings, Trash2, X, } from 'lucide-react';
import { toast } from 'sonner';
import { bulkDeletePosts, bulkUpdatePosts, fetchPosts, } from '../../lib/posts-service.js';
import { PostStatusBadge, PostTypeBadge, SeoScoreIndicator, paletteFor, } from '../../components/posts/badges.js';
import { ConfirmDialog } from '../../components/ui/ConfirmDialog.js';
const PAGE_SIZE = 10;
export function PostsListView({ onNavigate }) {
    const [activeType, setActiveType] = useState('all');
    const [search, setSearch] = useState('');
    const [debouncedSearch, setDebouncedSearch] = useState('');
    const [status, setStatus] = useState('all');
    const [author, setAuthor] = useState('all');
    const [sortBy, setSortBy] = useState('date');
    const [dir, setDir] = useState('desc');
    const [page, setPage] = useState(1);
    const [selected, setSelected] = useState(new Set());
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);
    const [refreshToken, setRefreshToken] = useState(0);
    const [pendingBulk, setPendingBulk] = useState(null);
    const debounceTimer = useRef(null);
    useEffect(() => {
        if (debounceTimer.current)
            clearTimeout(debounceTimer.current);
        debounceTimer.current = setTimeout(() => {
            setDebouncedSearch(search.trim());
            setPage(1);
        }, 250);
        return () => {
            if (debounceTimer.current)
                clearTimeout(debounceTimer.current);
        };
    }, [search]);
    // Re-fetch when any input that affects the result set changes.
    useEffect(() => {
        let cancelled = false;
        setLoading(true);
        setError(null);
        fetchPosts({
            postType: activeType,
            status,
            author,
            search: debouncedSearch,
            sortBy,
            sortDirection: dir,
            page,
            pageSize: PAGE_SIZE,
        })
            .then((res) => {
            if (cancelled)
                return;
            setData(res);
            setLoading(false);
        })
            .catch((err) => {
            if (cancelled)
                return;
            setError(err instanceof Error ? err.message : 'Failed to load posts');
            setLoading(false);
        });
        return () => {
            cancelled = true;
        };
    }, [activeType, status, author, debouncedSearch, sortBy, dir, page, refreshToken]);
    // Whenever the active type tab changes, reset paging to page 1.
    useEffect(() => {
        setPage(1);
        setSelected(new Set());
    }, [activeType, debouncedSearch, status, author]);
    // Selection is scoped to the rows visible on the current page: bulk
    // actions only ever touch `selectedPosts` (the current page's selected
    // rows), so clear the selection whenever the page changes. Without this
    // the bulk bar count keeps counting rows from earlier pages while the
    // publish/delete handlers silently operate on nothing.
    useEffect(() => {
        setSelected(new Set());
    }, [page]);
    const total = data?.total ?? 0;
    const totalAll = useMemo(() => (data?.types ?? []).reduce((sum, t) => sum + t.postCount, 0), [data?.types]);
    const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));
    const posts = data?.posts ?? [];
    const types = data?.types ?? [];
    const authors = data?.authors ?? [];
    const handleSort = useCallback((field) => {
        if (sortBy === field) {
            setDir(dir === 'asc' ? 'desc' : 'asc');
        }
        else {
            setSortBy(field);
            setDir(field === 'date' || field === 'seo' ? 'desc' : 'asc');
        }
    }, [sortBy, dir]);
    const clearFilters = () => {
        setSearch('');
        setStatus('all');
        setAuthor('all');
        setActiveType('all');
        setPage(1);
    };
    const filtersAreActive = debouncedSearch !== '' || status !== 'all' || author !== 'all' || activeType !== 'all';
    // ─── Selection / bulk actions ─────────────────────────────────────
    const toggleOne = (id) => setSelected((s) => {
        const next = new Set(s);
        if (next.has(id))
            next.delete(id);
        else
            next.add(id);
        return next;
    });
    const allOnPageSelected = posts.length > 0 && posts.every((p) => selected.has(p.id));
    const toggleAllOnPage = (checked) => setSelected((s) => {
        const next = new Set(s);
        if (checked)
            for (const p of posts)
                next.add(p.id);
        else
            for (const p of posts)
                next.delete(p.id);
        return next;
    });
    const selectedPosts = useMemo(() => posts.filter((p) => selected.has(p.id)), [posts, selected]);
    const runBulkUpdate = async (target) => {
        const ids = selectedPosts.map((p) => ({ postTypeId: p.postTypeId, id: p.id }));
        if (ids.length === 0)
            return;
        const verb = target === 'PUBLISHED' ? 'Publishing' : 'Updating';
        const promise = bulkUpdatePosts(ids, { status: target });
        toast.promise(promise, {
            loading: `${verb} ${ids.length} post${ids.length === 1 ? '' : 's'}…`,
            success: (r) => `${r.ok} updated${r.failed ? `, ${r.failed} failed` : ''}`,
            error: 'Bulk update failed',
        });
        await promise;
        setSelected(new Set());
        setRefreshToken((t) => t + 1);
    };
    const runBulkDelete = async () => {
        const ids = selectedPosts.map((p) => ({ postTypeId: p.postTypeId, id: p.id }));
        if (ids.length === 0)
            return;
        const promise = bulkDeletePosts(ids);
        toast.promise(promise, {
            loading: `Deleting ${ids.length} post${ids.length === 1 ? '' : 's'}…`,
            success: (r) => `${r.ok} deleted${r.failed ? `, ${r.failed} failed` : ''}`,
            error: 'Bulk delete failed',
        });
        await promise;
        setSelected(new Set());
        setRefreshToken((t) => t + 1);
    };
    // ─── Render ───────────────────────────────────────────────────────
    return (_jsxs("div", { className: "flex h-full flex-col gap-4 p-4 sm:p-6", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("h1", { className: "text-foreground text-xl font-medium tracking-tight sm:text-2xl", children: "Posts" }), _jsxs("p", { className: "text-muted-foreground text-sm", children: [totalAll, " total post", totalAll === 1 ? '' : 's', " across all types"] })] }), _jsx(TypeTabs, { types: types, totalAll: totalAll, active: activeType, onChange: setActiveType }), _jsx(ControlsRow, { search: search, onSearchChange: setSearch, status: status, onStatusChange: (s) => {
                    setStatus(s);
                    setPage(1);
                }, author: author, onAuthorChange: (a) => {
                    setAuthor(a);
                    setPage(1);
                }, authors: authors, resultCount: total, onManageTypes: () => onNavigate('/posts/types') }), selected.size > 0 && (_jsx(BulkActionBar, { count: selected.size, onClear: () => setSelected(new Set()), onPublish: () => runBulkUpdate('PUBLISHED'), onDraft: () => runBulkUpdate('DRAFT'), onDelete: () => setPendingBulk('delete') })), _jsxs("div", { className: "bg-card border-border overflow-hidden rounded-xl border shadow-sm", children: [_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full min-w-[760px] table-fixed", role: "table", "aria-label": "Posts", children: [_jsxs("colgroup", { children: [_jsx("col", { style: { width: '40px' } }), _jsx("col", {}), _jsx("col", { style: { width: '140px' } }), _jsx("col", { style: { width: '180px' } }), _jsx("col", { style: { width: '130px' } }), _jsx("col", { style: { width: '120px' } }), _jsx("col", { style: { width: '90px' } })] }), _jsx("thead", { className: "border-border bg-muted/40 border-b", children: _jsxs("tr", { className: "text-muted-foreground text-xs", children: [_jsx("th", { scope: "col", className: "px-3 py-2.5 text-left", children: _jsx("input", { type: "checkbox", "aria-label": "Select all posts on this page", checked: allOnPageSelected, onChange: (e) => toggleAllOnPage(e.target.checked), className: "border-border accent-primary rounded" }) }), _jsx("th", { scope: "col", className: "px-3 py-2.5 text-left", children: _jsx(SortHeader, { label: "Title", field: "title", current: sortBy, dir: dir, onSort: handleSort }) }), _jsx("th", { scope: "col", className: "px-3 py-2.5 text-left", children: _jsx(SortHeader, { label: "Type", field: "type", current: sortBy, dir: dir, onSort: handleSort }) }), _jsx("th", { scope: "col", className: "px-3 py-2.5 text-left", children: _jsx(SortHeader, { label: "Author", field: "author", current: sortBy, dir: dir, onSort: handleSort }) }), _jsx("th", { scope: "col", className: "px-3 py-2.5 text-left", children: _jsx(SortHeader, { label: "Status", field: "status", current: sortBy, dir: dir, onSort: handleSort }) }), _jsx("th", { scope: "col", className: "px-3 py-2.5 text-left", children: _jsx(SortHeader, { label: "Date", field: "date", current: sortBy, dir: dir, onSort: handleSort }) }), _jsx("th", { scope: "col", className: "px-3 py-2.5 text-left", children: _jsx(SortHeader, { label: "SEO", field: "seo", current: sortBy, dir: dir, onSort: handleSort }) })] }) }), _jsxs("tbody", { children: [loading && posts.length === 0 && _jsx(LoadingRows, {}), !loading && error && (_jsx("tr", { children: _jsxs("td", { colSpan: 7, className: "px-3 py-12 text-center", children: [_jsx("div", { className: "text-foreground mb-2 text-sm font-medium", children: error }), _jsx("button", { onClick: () => setRefreshToken((t) => t + 1), className: "text-primary text-sm hover:underline", children: "Try again" })] }) })), !loading && !error && posts.length === 0 && (_jsx(EmptyRow, { filtersActive: filtersAreActive, onClear: clearFilters, onNewPost: () => onNavigate('/posts/new') })), !loading &&
                                            !error &&
                                            posts.map((p) => (_jsx(PostRow, { post: p, selected: selected.has(p.id), onSelect: () => toggleOne(p.id), onOpen: () => onNavigate(`/posts/${p.postTypeId}/${p.id}/edit`) }, p.id)))] })] }) }), _jsx(PaginationFooter, { page: page, totalPages: totalPages, total: total, pageSize: PAGE_SIZE, onPage: setPage })] }), _jsx(ConfirmDialog, { open: pendingBulk === 'delete', title: `Delete ${selected.size} post${selected.size === 1 ? '' : 's'}?`, description: "This moves the selected posts to trash. They can be restored from the per-type list for 30 days.", confirmLabel: "Delete", destructive: true, onClose: () => setPendingBulk(null), onConfirm: () => {
                    void runBulkDelete();
                } })] }));
}
// ─── Sub-components ──────────────────────────────────────────────────
function SortHeader({ label, field, current, dir, onSort, }) {
    const active = current === field;
    const ariaSort = active ? (dir === 'asc' ? 'ascending' : 'descending') : 'none';
    return (_jsxs("button", { type: "button", onClick: () => onSort(field), "aria-sort": ariaSort, className: `hover:text-foreground inline-flex items-center gap-1 text-xs font-medium tracking-wider uppercase transition-colors ${active ? 'text-foreground' : 'text-muted-foreground'}`, children: [_jsx("span", { children: label }), active &&
                (dir === 'asc' ? (_jsx(ChevronUp, { className: "h-3 w-3", "aria-hidden": true })) : (_jsx(ChevronDown, { className: "h-3 w-3", "aria-hidden": true })))] }));
}
function TypeTabs({ types, totalAll, active, onChange, }) {
    return (_jsxs("div", { role: "tablist", "aria-label": "Filter posts by type", className: "border-border flex flex-wrap items-center gap-1 border-b pb-px", children: [_jsx(TabPill, { label: "All Posts", count: totalAll, active: active === 'all', onClick: () => onChange('all') }), types.map((t) => (_jsx(TabPill, { label: t.name, count: t.postCount, active: active === t.slug, onClick: () => onChange(t.slug) }, t.slug)))] }));
}
function TabPill({ label, count, active, onClick, }) {
    return (_jsxs("button", { type: "button", role: "tab", "aria-selected": active, onClick: onClick, className: `relative inline-flex items-center gap-2 rounded-md px-3 py-2 text-sm font-medium transition-colors ${active
            ? 'bg-primary/10 text-primary'
            : 'text-muted-foreground hover:text-foreground hover:bg-accent'}`, children: [_jsx("span", { children: label }), _jsx("span", { className: `rounded-full px-1.5 text-xs tabular-nums ${active ? 'bg-primary/15 text-primary' : 'bg-muted text-muted-foreground'}`, children: count })] }));
}
function ControlsRow({ search, onSearchChange, status, onStatusChange, author, onAuthorChange, authors, resultCount, onManageTypes, }) {
    return (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("div", { className: "relative max-w-sm flex-1", 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", value: search, onChange: (e) => onSearchChange(e.target.value), "aria-label": "Search posts", placeholder: "Search posts\u2026", className: "border-border bg-card text-foreground placeholder:text-muted-foreground focus:border-primary focus:ring-primary/30 w-full rounded-lg border py-2 pr-3 pl-9 text-sm shadow-sm focus:ring-2 focus:outline-none" })] }), _jsx(FilterSelect, { label: "Status", value: status, onChange: (v) => onStatusChange(v), options: [
                    { value: 'all', label: 'All' },
                    { value: 'PUBLISHED', label: 'Published' },
                    { value: 'DRAFT', label: 'Draft' },
                    { value: 'SCHEDULED', label: 'Scheduled' },
                ] }), _jsx(FilterSelect, { label: "Author", value: author, onChange: onAuthorChange, options: [
                    { value: 'all', label: 'All' },
                    ...authors.map((a) => ({ value: a.id, label: a.name })),
                ] }), _jsxs("div", { className: "ml-auto flex items-center gap-3", children: [_jsxs("button", { type: "button", onClick: onManageTypes, className: "border-border bg-background text-foreground hover:bg-accent inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-sm font-medium transition-colors", children: [_jsx(Settings, { className: "h-3.5 w-3.5", "aria-hidden": true }), "Manage types"] }), _jsxs("span", { className: "text-muted-foreground text-sm tabular-nums", children: [resultCount, " ", resultCount === 1 ? 'result' : 'results'] })] })] }));
}
function FilterSelect({ label, value, onChange, options, }) {
    return (_jsxs("label", { className: "text-foreground inline-flex items-center gap-1.5 text-sm", children: [_jsxs("span", { className: "text-muted-foreground", children: [label, ":"] }), _jsx("select", { value: value, onChange: (e) => onChange(e.target.value), className: "border-border bg-card text-foreground focus:border-primary focus:ring-primary/30 rounded-lg border px-2.5 py-1.5 text-sm shadow-sm focus:ring-2 focus:outline-none", children: options.map((o) => (_jsx("option", { value: o.value, children: o.label }, o.value))) })] }));
}
function BulkActionBar({ count, onClear, onPublish, onDraft, onDelete, }) {
    return (_jsxs("div", { className: "border-primary/30 bg-primary/5 flex items-center gap-2 rounded-lg border px-4 py-2", children: [_jsxs("span", { className: "text-foreground text-sm font-medium", children: [count, " selected"] }), _jsx("div", { className: "bg-border h-4 w-px", "aria-hidden": true }), _jsx("button", { type: "button", onClick: onPublish, className: "text-foreground hover:bg-accent rounded-md px-2 py-1 text-sm transition-colors", children: "Publish" }), _jsx("button", { type: "button", onClick: onDraft, className: "text-foreground hover:bg-accent rounded-md px-2 py-1 text-sm transition-colors", children: "Move to draft" }), _jsxs("button", { type: "button", onClick: onDelete, className: "text-destructive hover:bg-destructive/10 inline-flex items-center gap-1 rounded-md px-2 py-1 text-sm transition-colors", children: [_jsx(Trash2, { className: "h-3.5 w-3.5", "aria-hidden": true }), "Delete"] }), _jsxs("button", { type: "button", onClick: onClear, className: "text-muted-foreground hover:text-foreground ml-auto inline-flex items-center gap-1 rounded-md px-2 py-1 text-sm transition-colors", children: [_jsx(X, { className: "h-3.5 w-3.5", "aria-hidden": true }), "Clear"] })] }));
}
function PostRow({ post, selected, onSelect, onOpen, }) {
    const p = paletteFor(post.postTypeAccentColor);
    return (_jsxs("tr", { className: `border-border hover:bg-muted/30 group border-b transition-colors last:border-b-0 ${selected ? 'bg-primary/5' : ''}`, children: [_jsx("td", { className: "px-3 py-3", onClick: (e) => e.stopPropagation(), children: _jsx("input", { type: "checkbox", "aria-label": `Select "${post.title}"`, checked: selected, onChange: onSelect, className: "border-border accent-primary rounded" }) }), _jsx("td", { className: "px-3 py-3", children: _jsxs("button", { type: "button", onClick: onOpen, className: "flex w-full items-center gap-3 text-left", children: [_jsx("span", { "aria-hidden": true, className: `block h-9 w-9 shrink-0 rounded-md ${p.bg.split(' ')[0]}` }), _jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "text-foreground group-hover:text-primary block truncate text-sm font-medium", children: post.title }), _jsxs("span", { className: "text-muted-foreground block truncate text-xs", children: ["/", post.postTypeId, "/", post.slug || '—'] })] })] }) }), _jsx("td", { className: "px-3 py-3", children: _jsx(PostTypeBadge, { label: post.postTypeName, color: post.postTypeAccentColor }) }), _jsx("td", { className: "px-3 py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { "aria-hidden": true, className: "bg-muted text-muted-foreground inline-flex h-7 w-7 items-center justify-center rounded-full text-[11px] font-medium", children: post.authorInitials }), _jsx("span", { className: "text-foreground truncate text-sm", children: post.authorName })] }) }), _jsx("td", { className: "px-3 py-3", children: _jsx(PostStatusBadge, { status: post.status }) }), _jsx("td", { className: "text-muted-foreground px-3 py-3 text-sm tabular-nums", children: formatDateShort(post.updatedDate) }), _jsx("td", { className: "px-3 py-3", children: _jsx(SeoScoreIndicator, { score: post.seoScore }) })] }));
}
function LoadingRows() {
    const rows = Array.from({ length: 8 });
    return (_jsx(_Fragment, { children: rows.map((_, i) => (_jsx("tr", { className: "border-border border-b last:border-b-0", children: Array.from({ length: 7 }).map((__, j) => (_jsx("td", { className: "px-3 py-3", children: _jsx("div", { className: "bg-muted h-4 w-full animate-pulse rounded" }) }, j))) }, i))) }));
}
function EmptyRow({ filtersActive, onClear, onNewPost, }) {
    return (_jsx("tr", { children: _jsxs("td", { colSpan: 7, className: "px-3 py-16 text-center", children: [_jsx("div", { className: "text-foreground mb-2 text-sm font-medium", children: filtersActive ? 'No posts match the current filters.' : 'No posts yet.' }), _jsx("div", { className: "text-muted-foreground mb-4 text-sm", children: filtersActive
                        ? 'Try clearing the search or filters above.'
                        : 'Create your first post to get started.' }), filtersActive ? (_jsx("button", { type: "button", onClick: onClear, className: "text-primary text-sm hover:underline", children: "Clear filters" })) : (_jsx("button", { type: "button", onClick: onNewPost, className: "bg-primary text-primary-foreground hover:bg-primary/90 inline-flex items-center gap-1.5 rounded-lg px-3.5 py-1.5 text-sm font-medium shadow-sm transition-colors", children: "New Post" }))] }) }));
}
function PaginationFooter({ page, totalPages, total, pageSize, onPage, }) {
    const start = total === 0 ? 0 : (page - 1) * pageSize + 1;
    const end = Math.min(total, page * pageSize);
    return (_jsxs("div", { className: "border-border flex items-center justify-between border-t px-4 py-3 text-sm", children: [_jsxs("span", { className: "text-muted-foreground tabular-nums", children: [start, "\u2013", end, " of ", total, " post", total === 1 ? '' : 's'] }), _jsxs("nav", { className: "flex items-center gap-1", "aria-label": "Posts pagination", children: [_jsx("button", { type: "button", onClick: () => onPage(Math.max(1, page - 1)), disabled: page === 1, "aria-label": "Previous page", className: "border-border bg-background text-foreground hover:bg-accent inline-flex h-8 w-8 items-center justify-center rounded-md border transition-colors disabled:cursor-not-allowed disabled:opacity-40", children: _jsx(ChevronLeft, { className: "h-4 w-4", "aria-hidden": true }) }), pageNumbers(page, totalPages).map((p, idx) => p === '…' ? (_jsx("span", { "aria-hidden": true, className: "text-muted-foreground inline-flex h-8 w-8 items-center justify-center text-sm", children: "\u2026" }, `gap-${idx}`)) : (_jsx("button", { type: "button", onClick: () => onPage(p), "aria-current": p === page ? 'page' : undefined, className: `inline-flex h-8 w-8 items-center justify-center rounded-md text-sm tabular-nums transition-colors ${p === page
                            ? 'bg-primary text-primary-foreground'
                            : 'border-border bg-background text-foreground hover:bg-accent border'}`, children: p }, p))), _jsx("button", { type: "button", onClick: () => onPage(Math.min(totalPages, page + 1)), disabled: page === totalPages, "aria-label": "Next page", className: "border-border bg-background text-foreground hover:bg-accent inline-flex h-8 w-8 items-center justify-center rounded-md border transition-colors disabled:cursor-not-allowed disabled:opacity-40", children: _jsx(ChevronRight, { className: "h-4 w-4", "aria-hidden": true }) })] })] }));
}
// ─── Helpers ─────────────────────────────────────────────────────────
function formatDateShort(input) {
    const d = new Date(input);
    if (Number.isNaN(d.getTime()))
        return '—';
    return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
}
/**
 * Return up to 7 page-control entries: a sliding window around the
 * current page with ellipsis markers. Keeps the footer narrow even on
 * collections with hundreds of pages.
 */
export function pageNumbers(current, total) {
    if (total <= 7)
        return Array.from({ length: total }, (_, i) => i + 1);
    const out = [1];
    const start = Math.max(2, current - 1);
    const end = Math.min(total - 1, current + 1);
    if (start > 2)
        out.push('…');
    for (let i = start; i <= end; i++)
        out.push(i);
    if (end < total - 1)
        out.push('…');
    out.push(total);
    return out;
}
//# sourceMappingURL=PostsListView.js.map