import { useEffect, useMemo, useRef, useState } from 'react'
import { Link, router, usePage } from '@inertiajs/react'
import { ChevronRight, RotateCcw, Search, Settings } from 'lucide-react'
import { CustomizeColumnsModal } from '@/Components/Proto/Modals/CustomizeColumnsModal'
import { useColumnPrefs } from '@/lib/useColumnPrefs'
import AppLayout from '@/Layouts/AppLayout'
import { FilterPill } from '@/Components/ui/filter-pill'
import { ListFooter } from '@/Components/Table/ListFooter'
import { TOOLBAR_GEAR, TOOLBAR_FILTERS } from '@/Components/Table'
import { ExportButton } from '@/lib/excel/ExportButton'
import { CreateActionButton } from '@/Components/Table/CreateActionButton'
import { StatusBadge } from '@/Components/Proto/UI/StatusBadge'
import { DateText } from '@/Components/Proto/UI/DateText'
import { useResizableColumns, ColumnResizeGrip } from '@/lib/useResizableColumns'
import { useServerSort, SortButton } from '@/lib/ServerSort'
import { stripDefaults } from '@/lib/listParams'
import { complaintStatusTone, complaintTypeTone } from '@/lib/complaintTone'

// Sortable header ids — MUST mirror LoadsComplainDetail::SORT_COLUMNS. A key that is not in
// that allowlist falls back to the default sort SILENTLY: the arrow moves, the rows do not.
const SORTABLE = new Set([
    'reportNo', 'complainType', 'company', 'status', 'creator',
    'division', 'complainDate', 'inputDate', 'address', 'telp',
])

// Complaint & Returns — the shared list page for all FIVE list scopes (PRD §4.2): View Request
// (own, 250), View Head Dept (head, 422), View All (all, 251), View All Read Only (326), View
// All Report Read Only (385). One component; the `view` prop picks the route + title.
//
// Search, the three pills, sort and paging all resolve on the SERVER (ATURAN #26, GH #447).
// This screen used to receive every scoped row and filter/sort/slice them in the browser — a
// faithful copy of `Proto/Complaints/ViewAll.jsx`, which had no server to page against. The
// visible grammar is unchanged; only where the work happens moved.
const ROUTE_BY_VIEW = {
    own: 'complaints.index',
    head: 'complaints.head-dept',
    all: 'complaints.all',
    'all-readonly': 'complaints.all-readonly',
    'all-report': 'complaints.all-report',
}
// One detail route per scope — a row must open the detail THROUGH the menu the user is on, or
// they land on a screen gated by a grant they may not hold (and, before this split, on the
// wrong action buttons: the server used to infer the scope from whichever grant matched first).
const DETAIL_ROUTE_BY_VIEW = {
    own: 'complaints.show',
    head: 'complaints.head-dept.show',
    all: 'complaints.all.show',
    'all-readonly': 'complaints.all-readonly.show',
    'all-report': 'complaints.all-report.show',
}
const EXPORT_ROUTE_BY_VIEW = {
    own: 'complaints.export',
    head: 'complaints.export.head-dept',
    all: 'complaints.export.all',
    'all-readonly': 'complaints.export.all-readonly',
    'all-report': 'complaints.export.all-report',
}

// Data-driven columns — ⚙ opens CustomizeColumnsModal (hide/show + reorder), headers
// are also drag-to-reorder directly. Default widths feed the table-fixed layout.
const COLUMN_GROUPS = [{ id: 'queue', label: 'Queue' }]
const COLUMN_DEFS = [
    { id: 'reportNo', label: 'Report No', groupId: 'queue', required: true }, // row anchor
    { id: 'details', label: 'Details', groupId: 'queue' },
    { id: 'complainType', label: 'Complain Type', groupId: 'queue' },
    { id: 'company', label: 'Company Name', groupId: 'queue' },
    { id: 'status', label: 'Status', groupId: 'queue' },
    { id: 'creator', label: 'Creator', groupId: 'queue' },
    { id: 'division', label: 'Division', groupId: 'queue' },
    { id: 'complainDate', label: 'Complain Date', groupId: 'queue' },
    { id: 'inputDate', label: 'Input Date', groupId: 'queue' },
    { id: 'address', label: 'Address', groupId: 'queue' },
    { id: 'telp', label: 'Telp', groupId: 'queue' },
]
const COL_W = {
    reportNo: 110, details: 110, complainType: 150, company: 220, status: 140,
    creator: 150, division: 130, complainDate: 150, inputDate: 150, address: 260, telp: 140,
}
const COL_W_FALLBACK = 150
// `filters` MUST be in the allowlist: sort arrows and rows-per-page read from that prop, and
// `preserveState` would otherwise freeze them at their first-render value.
const RELOAD = { only: ['complaints', 'filters'], preserveState: true, preserveScroll: true, replace: true }

// A pill's value travels as a comma-separated list of IDS, never display names: a CompanyName
// can itself contain a comma, and filtering a list by a lookup's NAME makes that lookup the
// driving table (list-pagination.md scale rule #1).
const idsOf = (csv) => (csv ? String(csv).split(',').filter(Boolean).map(Number) : [])

// Truncated address; click to expand the full text (and click again to collapse).
function AddressCell({ value }) {
    const [open, setOpen] = useState(false)
    if (!value || value === '—') return <span className="text-muted-foreground">—</span>
    return (
        <button type="button" title={value}
            onClick={(e) => { e.stopPropagation(); setOpen((o) => !o) }}
            className={`block max-w-[260px] cursor-pointer text-left text-muted-foreground transition-colors hover:text-primary ${open ? 'whitespace-normal' : 'truncate'}`}>
            {value}
        </button>
    )
}

// Per-column cell renderer + td classes. `ctx.openDetail` navigates to the report.
const TD_CLASS = {
    reportNo: 'whitespace-nowrap !text-[13px] font-bold tabular-nums !text-primary',   // `!` wajib: [&_tbody_td]:text-foreground/text-[12px] di <table> = spesifisitas (0,1,2), menang atas kelas polos di td
    details: 'whitespace-nowrap text-center',
    complainType: 'whitespace-nowrap',
    company: 'whitespace-nowrap font-semibold text-foreground',
    status: 'whitespace-nowrap',
    creator: 'whitespace-nowrap text-foreground',
    division: 'whitespace-nowrap text-muted-foreground',
    complainDate: 'whitespace-nowrap',
    inputDate: 'whitespace-nowrap',
    address: 'max-w-[260px]',
    telp: 'whitespace-nowrap tabular-nums text-muted-foreground',
}
function renderCell(c, colId, ctx) {
    switch (colId) {
        case 'reportNo': return c.ID
        case 'details': return (
            <button type="button" onClick={() => ctx.openDetail(c)}
                className="group/link inline-flex items-center gap-1 text-[12px] font-bold text-primary transition-colors hover:underline">
                Details <ChevronRight className="size-3.5 transition-transform duration-200 group-hover/link:translate-x-0.5" aria-hidden="true" />
            </button>
        )
        case 'complainType': return c.ComplainTypeName ? <StatusBadge tone={complaintTypeTone(c.ComplainTypeName)}>{c.ComplainTypeName}</StatusBadge> : <span className="text-muted-foreground">—</span>
        case 'company': return c.CompanyName || '—'
        case 'status': return c.StatusName ? <StatusBadge tone={complaintStatusTone(c.StatusName)}>{c.StatusName}</StatusBadge> : <span className="text-muted-foreground">—</span>
        case 'creator': return c.Creator || '—'
        case 'division': return c.DivisionName || '—'
        case 'complainDate': return <DateText value={c.ComplainDate} />
        case 'inputDate': return <DateText value={c.InputDate} />
        case 'address': return <AddressCell value={c.CompanyAddress} />
        case 'telp': return c.Telp || '—'
        default: return '—'
    }
}

export default function Index({ complaints, view = 'own', viewTitle, filters = {}, filterOptions, canCreate = false, canCreateOthers = false }) {
    const routeName = ROUTE_BY_VIEW[view] ?? 'complaints.index'
    // This list's server-side defaults, so go() can leave them out of the URL (lib/listParams.js).
    const { listDefaults } = usePage().props
    const rows = complaints?.data ?? []

    // The search BOX is local; the committed term lives in `filters.search`. Enter and the
    // Search button are what commit it.
    const [searchDraft, setSearchDraft] = useState(filters.search || '')
    const [customizeOpen, setCustomizeOpen] = useState(false)

    // Every pill is MULTI-select (user decision 2026-08-20) — preserved across the move to the
    // server; list-pagination.md forbids narrowing a multi filter to single-choice.
    const division = useMemo(() => idsOf(filters.division), [filters.division])
    const company = useMemo(() => idsOf(filters.company), [filters.company])
    const creator = useMemo(() => idsOf(filters.creator), [filters.creator])

    // Resizable columns — drag a header's right edge to resize.
    const { widthOf, startResize, resizingId, resizeRef } = useResizableColumns(COL_W, COL_W_FALLBACK)
    // Column order + visibility — ⚙ modal AND direct header drag, persisted per user.
    const prefs = useColumnPrefs('complaintViewRequestColumns_v1', COLUMN_DEFS, { resizeGuardRef: resizeRef })
    const tableWidth = useMemo(() => prefs.visibleCols.reduce((sum, col) => sum + widthOf(col.id), 0), [prefs.visibleCols, widthOf])

    // Keep the box in step when the server answers with a different committed term (Reset, or
    // a back/forward navigation).
    useEffect(() => { setSearchDraft(filters.search || '') }, [filters.search])

    const go = (overrides = {}) => {
        const params = {
            // The COMMITTED search, not the live box: paginating or picking a pill must not
            // carry a half-typed term, nor silently drop one that is still filtering the rows.
            search: filters.search ?? '',
            division: filters.division ?? '',
            company: filters.company ?? '',
            creator: filters.creator ?? '',
            sort: filters.sort,
            dir: filters.dir,
            per_page: complaints?.per_page,
            ...overrides,
        }
        Object.keys(params).forEach((k) => {
            if (params[k] === '' || params[k] === null || params[k] === undefined) delete params[k]
        })
        router.get(route(routeName), stripDefaults(params, listDefaults), RELOAD)
    }

    // Sort travels to the SERVER: sortKey/sortDir come from `filters`, never local state —
    // `preserveState` would freeze a local copy while the rows underneath it changed.
    const { sortKey, sortDir, toggleSort } = useServerSort(filters, go)

    const openDetail = (c) => router.visit(route(DETAIL_ROUTE_BY_VIEW[view] ?? 'complaints.show', c.ID))

    const hasFilter = Boolean(filters.search || filters.division || filters.company || filters.creator || searchDraft)
    const resetFilters = () => {
        setSearchDraft('')
        router.get(route(routeName), {}, RELOAD)
    }

    // Export follows the SCREEN: the same search + pills the server is applying, so "export"
    // means "export what I am looking at" rather than the whole scope.
    const exportParams = {}
    if (filters.search) exportParams.search = filters.search
    if (filters.division) exportParams.division = filters.division
    if (filters.company) exportParams.company = filters.company
    if (filters.creator) exportParams.creator = filters.creator

    return (
        <section className="flex min-w-0 flex-col gap-[18px]">
            <header className="flex items-start justify-between gap-4">
                <div>
                    <p className="m-0 mb-1.5 flex items-center gap-2 text-xs font-semibold text-muted-foreground">
                        <Link href={route(routeName)} className="no-underline hover:text-primary">Complain &amp; Returns</Link>
                        <span aria-hidden="true">›</span>
                        <span className="text-foreground">{viewTitle}</span>
                    </p>
                    <h1 className="m-0 text-xl font-bold leading-[1.2] text-card-foreground">{viewTitle}</h1>
                </div>
                <div className="flex flex-wrap items-center justify-end gap-2">
                    <ExportButton specKey="complaintExport" url={route(EXPORT_ROUTE_BY_VIEW[view] ?? 'complaints.export')}
                        params={exportParams} label="Export" className="h-9 px-4 text-[11px]" />
                    <CreateActionButton
                        canCreate={canCreate}
                        label="New Complaint"
                        href={route('complaints.create')}
                        variants={[{ key: 'others', label: 'For Others', can: canCreateOthers, href: route('complaints.create-others') }]}
                    />
                </div>
            </header>

            {/* Filter bar + list — one merged card */}
            <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-5 py-4">
                    <label className="relative inline-flex h-8 w-[260px] max-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" aria-label="Search complaint">
                        <Search aria-hidden="true" className="size-3.5 shrink-0" />
                        <input type="search" placeholder="Search report, company, address…" autoComplete="off" value={searchDraft}
                            onChange={(e) => setSearchDraft(e.target.value)}
                            onKeyDown={(e) => { if (e.key === 'Enter') go({ search: searchDraft.trim(), page: 1 }) }}
                            className="min-w-0 flex-1 bg-transparent text-[12.5px] font-medium text-foreground outline-none placeholder:text-muted-foreground/70" />
                    </label>
                    {/* ⚙ on line ONE: DOM position, not `order` — a flex line is filled in order
                        sequence and TOOLBAR_FILTERS is w-full. See TOOLBAR_ROW in Components/Table. */}
                    <button type="button" onClick={() => setCustomizeOpen(true)} title="Customize columns" aria-label="Customize columns"
                        className={TOOLBAR_GEAR}>
                        <Settings className="size-3.5" strokeWidth={2.5} />
                    </button>
                    <div className={TOOLBAR_FILTERS}>
                        <button type="button" onClick={() => go({ search: searchDraft.trim(), page: 1 })}
                            className="inline-flex h-8 shrink-0 items-center gap-1.5 rounded-full bg-linear-to-br from-violet-500 to-primary px-4 text-xs font-bold text-white shadow-sm transition-[filter] hover:brightness-105">
                            <Search aria-hidden="true" className="size-3.5" /> Search
                        </button>
                        <FilterPill label="Division" value={division} options={filterOptions?.divisions ?? []}
                            onChange={(v) => go({ division: v.join(','), page: 1 })} />
                        <FilterPill label="Company" value={company} options={filterOptions?.companies ?? []}
                            onChange={(v) => go({ company: v.join(','), page: 1 })} />
                        <FilterPill label="Creator" value={creator} options={filterOptions?.creators ?? []}
                            onChange={(v) => go({ creator: v.join(','), page: 1 })} />
                        {hasFilter && (
                            <button type="button" onClick={resetFilters}
                                title="Reset all active filters"
                                className="inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full px-2.5 text-[12.5px] font-semibold text-muted-foreground transition-colors hover:bg-danger/10 hover:text-danger-text">
                                <RotateCcw className="size-3.5" />
                                <span>Reset filters</span>
                            </button>
                        )}
                    </div>
                </div>

                <div className="overflow-x-auto">
                    <table style={{ minWidth: tableWidth }} className="w-full table-fixed border-separate border-spacing-0 [&_tbody_td]:overflow-hidden [&_tbody_td]:border-b [&_tbody_td]:border-border/60 [&_tbody_td]:px-3.5 [&_tbody_td]:py-[16px] [&_tbody_td]:align-middle [&_tbody_td]:text-[12px] [&_tbody_td]:text-foreground [&_tbody_tr:last-child_td]:border-b-0 [&_tbody_tr:hover_td]:bg-secondary/60 [&_tbody_tr:nth-child(even)_td]:bg-secondary/25 [&_thead_th]:whitespace-nowrap [&_thead_th]:bg-[color-mix(in_srgb,var(--color-secondary)_50%,var(--color-card))] [&_thead_th]:px-3.5 [&_thead_th]:py-3 [&_thead_th]:text-left [&_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:first-child]:pl-7 [&_tbody_td:last-child]:pr-5">
                        <colgroup>
                            {prefs.visibleCols.map((col) => <col key={col.id} style={{ width: widthOf(col.id) }} />)}
                        </colgroup>
                        <thead>
                            <tr>
                                {prefs.visibleCols.map((col) => (
                                    <th key={col.id} {...prefs.dragProps(col.id)}
                                        className={`group/col relative ${prefs.dragClass(col.id)}`}>
                                        {SORTABLE.has(col.id)
                                            ? <SortButton id={col.id} label={col.label} sortKey={sortKey} sortDir={sortDir} onToggle={toggleSort} />
                                            : col.label}
                                        <ColumnResizeGrip onMouseDown={(e) => startResize(e, col.id)} active={resizingId === col.id} />
                                    </th>
                                ))}
                            </tr>
                        </thead>
                        <tbody>
                            {rows.length === 0 ? (
                                <tr>
                                    <td colSpan={prefs.visibleCols.length} className="px-4 py-10 text-center italic text-muted-foreground">No complaint reports match the filter.</td>
                                </tr>
                            ) : (
                                rows.map((c) => (
                                    <tr key={c.ID} onClick={() => openDetail(c)} className="cursor-pointer group/row">
                                        {prefs.visibleCols.map((col) => (
                                            <td key={col.id} className={TD_CLASS[col.id]}
                                                onClick={col.id === 'details' ? (e) => e.stopPropagation() : undefined}>
                                                {renderCell(c, col.id, { openDetail })}
                                            </td>
                                        ))}
                                    </tr>
                                ))
                            )}
                        </tbody>
                    </table>
                </div>

                <ListFooter
                    page={complaints?.current_page} totalPages={complaints?.last_page} onPage={(p) => go({ page: p })}
                    pageSize={complaints?.per_page} onPageSize={(n) => go({ per_page: n, page: 1 })} pageSizeOptions={[10, 20, 50, 100]}
                    total={complaints?.total} from={complaints?.from} to={complaints?.to} itemLabel="results" />
            </article>

            <CustomizeColumnsModal
                open={customizeOpen}
                onClose={() => setCustomizeOpen(false)}
                groups={COLUMN_GROUPS}
                definitions={COLUMN_DEFS}
                state={prefs.columnState}
                onApply={prefs.applyColumns}
                onReset={prefs.resetColumns}
            />
        </section>
    )
}

Index.layout = [AppLayout]
