import { useMemo, useState } from 'react';
import { Link, router } from '@inertiajs/react';
import { ArrowDown, ArrowUp, ChevronDown, ChevronsUpDown, Search, Settings } from 'lucide-react';
import { cn } from '@/lib/utils';
import { ListFooter } from '@/Components/Table/ListFooter';
import { CustomizeColumnsModal } from '@/Components/Proto/Modals/CustomizeColumnsModal';
import { useResizableColumns, ColumnResizeGrip } from '@/lib/useResizableColumns';
import { useStickyColShadow } from '@/lib/useStickyColShadow';
import { DateRangePill, IsOrderPill, OptionPill, SelectPill } from './QuotationListPills';
import { FilterPill } from '@/Components/ui/filter-pill';
import { useQuotationListColumns } from './useQuotationListColumns';
import { useQuotationListQuery } from './useQuotationListQuery';

// Default per-column pixel widths for the resizable table-fixed layout (overridable
// by drag). Covers the union of every column id used by this shell's consumers — the
// simple quotation list (quotationListConfig) and the wide line-item detail report
// (quotationDetailListConfig). Ids absent here fall back to COL_W_FALLBACK.
const COL_W = {
    // shared identifiers / metadata / order
    no: 70, id: 90, company: 230, status: 150, feedback: 150, creator: 130, sales: 130,
    division: 120, industry: 150, companyCategory: 160, tanggal: 120,
    poNo: 150, poDate: 140, deliveryDate: 130, comment: 180, productList: 320,
    // detail report — product
    principal: 220, product: 220, application: 140, qtyPacking: 120, unitPacking: 140,
    // detail report — pricing
    priceUsd: 130, usdRate: 120, priceIdr: 140, orderQty: 120, deliveryFee: 130,
    // detail report — metadata / order
    address: 240, isOrder: 100, remarks: 220, remarkInternal: 220, history: 110,
    // company rebate lists — history is a clock-chip popover now (was 420px of inline text)
    rebateHistory: 110, remark: 200,
    // Both rebate modules ship the line items twice: `products` is the chip (a count, so it
    // needs almost nothing) and `productsList` prints them inline, which needs real room — at
    // the 150px fallback every product name wrapped after two words. Users can still drag
    // either edge; this is only the starting width.
    products: 96, productsList: 300, invoiceHistory: 110,
    insertDate: 125, salesRebateDate: 135, validityStart: 125, validityEnd: 125,
};
const COL_W_FALLBACK = 150;

const TABLE_CLASS = 'w-full table-fixed border-separate border-spacing-0 [&_tbody_td]:overflow-hidden [&_td]:whitespace-nowrap [&_td]:px-3.5 [&_td]:py-3 [&_td]:text-left [&_td]:text-[12px] [&_td.col-edit]:text-center [&_td.col-no]:text-center [&_td.col-no]:font-extrabold [&_td.col-no]:text-muted-foreground [&_td.col-smart]:min-w-50 [&_td.col-smart]:border-l [&_td.col-smart]:border-border [&_td.col-smart]:px-3.5 [&_td.col-smart]:py-3 [&_td.col-smart]:align-middle [&_th]:whitespace-nowrap [&_th]:px-3.5 [&_th]:py-3 [&_th]:text-left [&_th]:text-[11px] [&_th.col-name]:sticky [&_th.col-name]:left-0 [&_th.col-name]:z-3 [&_th.col-smart]:min-w-50 [&_th.col-smart]:border-l [&_th.col-smart]:border-border [&_thead_.group-header_th]:border-b [&_thead_.group-header_th]:border-border [&_thead_.group-header_th]:bg-secondary [&_thead_.group-header_th]:px-3.5 [&_thead_.group-header_th]:py-1.5 [&_thead_.group-header_th]:text-center [&_thead_.group-header_th]:text-[10px] [&_thead_.group-header_th]:font-extrabold [&_thead_.group-header_th]:uppercase [&_thead_.group-header_th]:tracking-[0.04em] [&_thead_.group-header_th]:text-primary [&_thead_.group-header_th:not(:empty)]:bg-accent [&_thead_th]:sticky [&_thead_th]:top-0 [&_thead_th]:z-2 [&_thead_th]:bg-[color-mix(in_srgb,var(--color-secondary)_50%,var(--color-card))] [&_thead_th]:text-[11px] [&_thead_th]:font-semibold [&_thead_th]:uppercase [&_thead_th]:tracking-wide [&_thead_th]:text-muted-foreground [&_thead_tr:first-child_th:first-child]:rounded-tl-full [&_thead_tr:first-child_th:first-child]:pl-7 [&_thead_tr:first-child_th:last-child]:rounded-tr-full [&_thead_tr:first-child_th:last-child]:pr-5 [&_thead_tr:last-child_th:first-child]:rounded-bl-full [&_thead_tr:last-child_th:first-child]:pl-7 [&_thead_tr:last-child_th:last-child]:rounded-br-full [&_thead_tr:last-child_th:last-child]:pr-5 [&_tbody_td]:border-b [&_tbody_td]:border-border/60 [&_tbody_td:last-child]:pr-5 [&_tbody_td]:px-3.5 [&_tbody_td]:py-[16px] [&_tbody_td]:text-[12px] [&_tbody_td]:text-card-foreground [&_tbody_td.col-address]:max-w-55 [&_tbody_td.col-name]:sticky [&_tbody_td.col-name]:left-0 [&_tbody_td.col-name]:z-1 [&_tbody_td.col-name]:bg-card [&_tbody_td.col-name]:font-semibold [&_tbody_td.col-name]:text-card-foreground [&_tbody_td:first-child]:pl-7 [&_tbody_td:first-child]:text-foreground [&_tbody_tr:hover_td]:bg-secondary/60 [&_tbody_tr:hover_td.col-name]:bg-[color-mix(in_srgb,var(--color-secondary)_60%,var(--color-card))] [&_tbody_tr:nth-child(even)_td]:bg-secondary/25 [&_tbody_tr:nth-child(even)_td.col-name]:bg-[color-mix(in_srgb,var(--color-secondary)_25%,var(--color-card))]';

const SCROLL_SHADOW = 'overflow-x-auto rounded-xl [background:linear-gradient(to_right,var(--card)_30%,transparent),linear-gradient(to_right,transparent,var(--card)_70%)_right,radial-gradient(farthest-side_at_0_50%,rgba(0,0,0,0.12),transparent),radial-gradient(farthest-side_at_100%_50%,rgba(0,0,0,0.12),transparent)_right] [background-attachment:local,local,scroll,scroll] [background-repeat:no-repeat] [background-size:40px_100%,40px_100%,14px_100%,14px_100%]';

/**
 * The shared MenuQuotations list shell: breadcrumb + title + header-action slot,
 * quick search, pill toolbar, customize/reorder-able table, pagination, per-page.
 * Per-menu variation (columns, pills, cell rendering, row link) comes in via
 * props from the page wrapper; this component owns only the shared behavior.
 */
export function QuotationListPage({
    title,
    breadcrumb = [],
    headerActions = null,
    routeName,
    paginator,
    dataProp,
    filters,
    columns,
    storageKey,
    renderCell,
    rowHref,
    rowKey = (row) => row.id,
    rowClass = null,
    pills = [],
    emptyText = 'No records found.',
    searchPlaceholder = 'Search...',
    searchAriaLabel = 'Search',
    pageSizeOptions = [5, 10, 15, 20],
    renderColumnOption = null,
    widthOverride = null,
}) {
    const filterKeys = pills.flatMap((p) => (p.type === 'dateRange'
        ? ['date_from', 'date_to']
        : [p.key ?? (p.type === 'isOrder' ? 'is_order' : null)])).filter(Boolean);

    // Partial-reload allowlist. `dataProp` names the paginator prop as the controller
    // sends it — this shell serves three different names (`quotations`, `details`,
    // `rebates`), so it cannot be inferred from the paginator VALUE alone.
    //
    // `filters` is not optional here: sortKey/sortDir/pageSize below are read from it,
    // not from local state, so leaving it out would freeze the sort arrow and the
    // rows-per-page select on whatever the first render saw.
    const only = dataProp ? [dataProp, 'filters'] : null;

    const { search, values, setValue, go, onSearchChange, onSort, sortKey, sortDir, pageSize } =
        useQuotationListQuery({ routeName, filters, paginator, filterKeys, only });

    const { columnState, visibleCols, applyColumns, resetColumns, reorderColumns } =
        useQuotationListColumns(storageKey, columns.defs, columns.defaultVisible);

    // Resizable columns — drag a header's right edge (matches the Feedback list).
    const { widthOf, startResize, resizingId, resizeRef } = useResizableColumns(COL_W, COL_W_FALLBACK);
    // A column whose rendering mode is switchable needs a different starting width per mode
    // (a hover chip wants ~96px, the same data printed inline wants ~300). `widthOverride`
    // lets the page raise the floor for one column without touching COL_W, and a user drag
    // still wins because widthOf() is consulted first.
    const colWidth = (id) => {
        const w = widthOf(id);
        const min = widthOverride?.(id);
        return min ? Math.max(w, min) : w;
    };
    const tableWidth = useMemo(() => visibleCols.reduce((s, c) => s + colWidth(c.id), 0), [visibleCols, widthOf]);

    // Sticky-column divider appears only while scrolled horizontally.
    const { scrollRef, shadowClass } = useStickyColShadow();

    const [activePill, setActivePill] = useState(null);
    const [customizeOpen, setCustomizeOpen] = useState(false);
    const [dragColIdx, setDragColIdx] = useState(null);
    const [dragOverColIdx, setDragOverColIdx] = useState(null);

    const togglePill = (key) => setActivePill(activePill === key ? null : key);
    const pickFor = (key) => (val) => {
        setValue(key, val);
        setActivePill(null);
        go({ [key]: val, page: 1 });
    };

    const renderPill = (pill) => {
        const key = pill.key ?? (pill.type === 'isOrder' ? 'is_order' : pill.type);
        switch (pill.type) {
            case 'select':
                return <SelectPill key={key} label={pill.label} value={values[key]} options={pill.options || []} open={activePill === key} onToggle={() => togglePill(key)} onPick={pickFor(key)} />;
            case 'option':
                return <OptionPill key={key} label={pill.label} value={values[key]} options={pill.options || []} searchable={pill.searchable} open={activePill === key} onToggle={() => togglePill(key)} onPick={pickFor(key)} />;
            case 'multi': {
                // Multi-select filter. The value travels as a comma-separated id list, which is
                // what PaginatesList::multiIds() parses server-side. Added for Generate Company
                // Rebate's View All Status filter, which is genuinely multi-select in legacy
                // (SearchStatus[]) — an OptionPill would have silently narrowed it to one.
                // FilterPill owns its own open state, so it does not use activePill.
                const selected = String(values[key] ?? '').split(',').filter(Boolean);

                return (
                    <FilterPill
                        key={key}
                        label={pill.label}
                        value={selected}
                        options={pill.options || []}
                        onChange={(ids) => {
                            const next = ids.join(',');
                            setValue(key, next);
                            go({ [key]: next, page: 1 });
                        }}
                    />
                );
            }
            case 'isOrder':
                return (
                    <IsOrderPill
                        key={key}
                        label={pill.label}
                        active={Boolean(values[key])}
                        onToggle={() => {
                            const next = !values[key];
                            setValue(key, next);
                            go({ [key]: next, page: 1 });
                        }}
                    />
                );
            case 'dateRange':
                return (
                    <DateRangePill
                        key="dateRange"
                        label={pill.label}
                        from={values.date_from}
                        to={values.date_to}
                        open={activePill === 'dateRange'}
                        onToggle={() => togglePill('dateRange')}
                        onApply={(from, to) => {
                            setValue('date_from', from);
                            setValue('date_to', to);
                            setActivePill(null);
                            go({ date_from: from, date_to: to, page: 1 });
                        }}
                    />
                );
            default:
                return null;
        }
    };

    const totalPages = paginator.last_page;
    const currentPage = paginator.current_page;
    const rows = paginator.data;

    return (
        <section className="flex min-w-0 flex-col gap-[18px]">
            <header className="flex flex-wrap items-start justify-between gap-3">
                <div className="min-w-0">
                    <p className="m-0 mb-1.5 flex items-center gap-2 text-xs font-semibold text-muted-foreground">
                        {breadcrumb.map((item, i) => {
                            const last = i === breadcrumb.length - 1;
                            return (
                                <span key={item.label} className="flex items-center gap-2">
                                    {item.href && !last
                                        ? <Link href={item.href} className="no-underline hover:text-primary">{item.label}</Link>
                                        : <span className={cn(last && 'text-foreground')}>{item.label}</span>}
                                    {!last && <span aria-hidden="true">›</span>}
                                </span>
                            );
                        })}
                    </p>
                    <h1 className="m-0 text-xl font-bold leading-[1.2] text-card-foreground">{title}</h1>
                </div>
                {headerActions && (
                    <div className="flex flex-wrap justify-end gap-2">
                        {headerActions}
                    </div>
                )}
            </header>

            {/* ── Filter Pills Toolbar ─────────────────────────────────────────── */}
            {activePill && (
                <div className="fixed inset-0 z-49" onClick={() => setActivePill(null)} />
            )}

            <article className="overflow-hidden rounded-2xl border border-border bg-card shadow-sm">
              <div className="flex flex-wrap items-center gap-2.5 border-b border-border/50 px-4 py-3.5 sm:px-5 sm:py-4">
                {/* Full width on phones (search + pills each get their own row);
                    shares the row from sm up. */}
                <label className="relative inline-flex h-8 w-full items-center gap-2 rounded-full border border-transparent bg-muted/60 px-3.5 text-muted-foreground transition-colors hover:bg-muted focus-within:border-primary/40 focus-within:bg-card sm:w-auto sm:min-w-[200px] sm:max-w-[320px] sm:flex-1" aria-label={searchAriaLabel}>
                    <Search aria-hidden="true" className="size-3.5 shrink-0" />
                    <input
                        type="search"
                        placeholder={searchPlaceholder}
                        value={search}
                        onChange={(e) => onSearchChange(e.target.value)}
                        className="min-w-0 flex-1 bg-transparent text-[12.5px] font-medium text-foreground outline-none placeholder:text-muted-foreground/70"
                    />
                </label>

                <div className="flex w-full flex-wrap items-center gap-2.5 sm:w-auto sm:flex-1">
                    {pills.map(renderPill)}
                    <div className="ml-0 sm:ml-auto inline-flex items-center gap-0.5">
                        <button type="button" onClick={() => setCustomizeOpen(true)} title="Customize columns" aria-label="Customize columns"
                            className="grid size-7 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground">
                            <Settings aria-hidden="true" className="size-3.5" strokeWidth={2.5} />
                        </button>
                    </div>
                </div>
              </div>

              <div className="px-5 pb-5 pt-4">
                <div ref={scrollRef} className={SCROLL_SHADOW}>
                    {/* minWidth, not width (user 2026-08-24: "ga full width tabelnya"). tableWidth is
                        the SUM of the column widths, so pinning `width` to it made the table stop
                        growing once the card was wider than that sum — measured on a 2560px screen:
                        card 2216px, table stuck at 1510px, 706px of empty card to its right. As a
                        minimum it still overflows and scrolls on narrow screens, and `w-full` lets
                        table-fixed hand the surplus back to the columns on wide ones. */}
                    <table style={{ minWidth: tableWidth }} className={cn('w-full', TABLE_CLASS, shadowClass)}>
                        <colgroup>
                            {visibleCols.map((col) => <col key={col.id} style={{ width: colWidth(col.id) }} />)}
                        </colgroup>
                        <thead>
                            <tr>
                                {visibleCols.map((col, i) => {
                                    const sortable = col.sortable !== false;
                                    const isDragging = dragColIdx === i;
                                    const isDragOver = dragOverColIdx === i && dragColIdx !== i;
                                    return (
                                        <th
                                            key={col.id}
                                            draggable
                                            onDragStart={(e) => { if (resizeRef.current) { e.preventDefault(); return; } setDragColIdx(i); }}
                                            onDragOver={(e) => { e.preventDefault(); setDragOverColIdx(i); }}
                                            onDrop={() => { reorderColumns(dragColIdx, i); setDragColIdx(null); setDragOverColIdx(null); }}
                                            onDragEnd={() => { setDragColIdx(null); setDragOverColIdx(null); }}
                                            className={cn(
                                                col.sticky && 'col-name',
                                                col.numeric && 'text-right',
                                                isDragging && 'opacity-45',
                                                isDragOver && 'bg-accent text-accent-foreground',
                                                'group/col relative h-auto cursor-grab select-none active:cursor-grabbing'
                                            )}
                                        >
                                            <span className="inline-block align-middle">
                                                {sortable ? (
                                                    <button type="button" onClick={() => onSort(col.id)} className="inline-flex items-center gap-1 bg-transparent p-0 font-[inherit] text-[inherit] uppercase border-none text-left">
                                                        <span>{col.label}</span>
                                                        {sortKey === col.id
                                                            ? (sortDir === 'asc' ? <ArrowUp className="size-3 text-muted-foreground" /> : <ArrowDown className="size-3 text-muted-foreground" />)
                                                            : <ChevronsUpDown className="size-3 opacity-40" />}
                                                    </button>
                                                ) : (
                                                    <span className="uppercase">{col.label}</span>
                                                )}
                                            </span>
                                            <ColumnResizeGrip onMouseDown={(e) => startResize(e, col.id)} active={resizingId === col.id} />
                                        </th>
                                    );
                                })}
                            </tr>
                        </thead>
                        <tbody>
                            {rows.length === 0 ? (
                                <tr>
                                    <td colSpan={visibleCols.length} className="py-8 !text-center">{emptyText}</td>
                                </tr>
                            ) : (
                                rows.map((row, rowIndex) => (
                                    <tr
                                        key={rowKey(row)}
                                        onClick={rowHref ? () => router.visit(rowHref(row)) : undefined}
                                        className={cn(rowHref && 'cursor-pointer', rowClass?.(row))}
                                    >
                                        {visibleCols.map((col) => (
                                            <td key={col.id} className={cn(col.sticky && 'col-name', col.numeric && '!text-right tabular-nums', col.cellClass)}>
                                                {renderCell(row, col.id, { rowNumber: (paginator.from || 0) + rowIndex })}
                                            </td>
                                        ))}
                                    </tr>
                                ))
                            )}
                        </tbody>
                    </table>
                </div>

                {/* Footer — design-system ListFooter (Showing · ghost pager · rows-per-page).
                    Sits inside the card's padded px-5/pb-5 wrapper, so drop its border-t
                    and match the old footer's inner spacing. */}
                <ListFooter
                    className="border-t-0 px-1 pb-1 pt-4"
                    page={currentPage} totalPages={totalPages} onPage={(p) => go({ page: p })}
                    pageSize={Number(pageSize)} onPageSize={(n) => go({ per_page: n, page: 1 })} pageSizeOptions={pageSizeOptions}
                    total={paginator.total} itemLabel="entries"
                    // Read the range the SERVER actually returned instead of recomputing it
                    // from page × pageSize. On a partial reload the rows arrive before local
                    // state settles, and an arithmetic range can briefly disagree with the
                    // rows on screen — the last page is the obvious case, where the formula
                    // claims a full page.
                    from={paginator.from} to={paginator.to}
                />
              </div>
            </article>

            {/* renderColumnOption: the per-column extra control CustomizeColumnsModal supports
                (see its docblock). Pages use it to offer a column TWO ways of rendering the same
                data — one title, one row in the settings, a mode picker beneath it — the way
                /sample-orders does for Sample List. Pages that pass nothing render as before. */}
            <CustomizeColumnsModal open={customizeOpen} onClose={() => setCustomizeOpen(false)} groups={columns.groups} definitions={columns.defs} state={columnState} onApply={applyColumns} onReset={resetColumns} renderOption={renderColumnOption} />
        </section>
    );
}
