/**
 * Shared table primitives for Index list pages.
 * Matches Phase 8 design system: rounded thead pill corners, zebra striping,
 * hover tint, breadcrumb topbar, pill-style action buttons.
 *
 * Used by: Banks/Index, Roles/Index, Menus/Index, RoleMenus/Index.
 */

import { Children, cloneElement, createContext, isValidElement, useContext, useLayoutEffect, useRef, useState } from 'react'
import { Link } from '@inertiajs/react'
import { Check, Search, X } from '@/Components/Icons'
import { Eye, Pencil, Trash2, RotateCcw } from 'lucide-react'
import { NativeSelect } from '@/Components/ui/native-select';
import { SortButton } from '@/lib/ClientSort'

// ──────────────────────────────────────────────────────────────
// Topbar — breadcrumb + h1 + primary action (right side)
// ──────────────────────────────────────────────────────────────

export function TopBar({ title, breadcrumb, action }) {
    return (
        <header className="flex flex-wrap items-center justify-between gap-4 mb-[18px]">
            <div>
                {breadcrumb && (
                    <p className="flex items-center gap-2 text-text-muted text-xs font-semibold mb-1.5">
                        {breadcrumb}
                    </p>
                )}
                <h1 className="m-0 text-text-heading font-semibold text-2xl leading-tight">{title}</h1>
            </div>
            {action && <div className="flex flex-wrap justify-end gap-2">{action}</div>}
        </header>
    )
}

export function Crumb({ href, children }) {
    return (
        <Link href={href} className="text-text-muted no-underline hover:text-primary">
            {children}
        </Link>
    )
}

export function CrumbCurrent({ children }) {
    return <span className="text-text-body">{children}</span>
}

export function CrumbSep() {
    return <span aria-hidden="true">›</span>
}

// ──────────────────────────────────────────────────────────────
// Primary action button (gradient pill)
// ──────────────────────────────────────────────────────────────

export function PrimaryButton({ href, onClick, children, type = 'button' }) {
    const className = "inline-flex h-9 items-center justify-center gap-1.5 rounded-lg border border-transparent bg-linear-to-br from-violet-500 to-primary px-4 text-xs font-bold text-white shadow-sm transition-[filter] hover:brightness-105 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
    if (href) {
        return <Link href={href} className={className}>{children}</Link>
    }
    return <button type={type} onClick={onClick} className={className}>{children}</button>
}

// ──────────────────────────────────────────────────────────────
// Card wrapper for list table
// ──────────────────────────────────────────────────────────────

export function ListCard({ children }) {
    return (
        <article className="bg-card shadow-sm border border-border rounded-2xl px-5 pt-[18px] pb-5">
            {children}
        </article>
    )
}

// ──────────────────────────────────────────────────────────────
// Toolbar (filter row above table)
// ──────────────────────────────────────────────────────────────

export function Toolbar({ children, rightActions }) {
    return (
        <div className="flex items-end justify-between gap-2.5 pb-3.5 flex-wrap -mx-5 px-5 border-b border-border/40">
            <div className="flex items-end gap-2 flex-wrap flex-1">{children}</div>
            {rightActions && <div className="flex items-end gap-2">{rightActions}</div>}
        </div>
    )
}

// ──────────────────────────────────────────────────────────────
// Form controls (matched to Toolbar height = h-9)
// ──────────────────────────────────────────────────────────────

export function SearchField({ value, onChange, onEnter, placeholder, width = 'w-[min(260px,100%)]' }) {
    return (
        <label className={`relative inline-flex h-9 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 ${width}`}>
            <Search size={14} className="shrink-0" />
            <input
                type="search"
                value={value}
                onChange={(e) => onChange(e.target.value)}
                onKeyDown={onEnter ? (e) => { if (e.key === 'Enter') { e.preventDefault(); onEnter() } } : undefined}
                placeholder={placeholder}
                className="min-w-0 flex-1 bg-transparent text-[12.5px] font-medium text-foreground outline-none placeholder:text-muted-foreground/70"
            />
        </label>
    )
}

export function SelectField({ value, onChange, options, minWidth = 'min-w-[120px]', ariaLabel }) {
    return (
        <label className="inline-flex flex-col">
            <span className="sr-only">{ariaLabel}</span>
            <NativeSelect
                value={value}
                onChange={(e) => onChange(e.target.value)}
                className={`h-9 px-3 rounded-[8px] border border-border bg-surface text-sm text-text-body ${minWidth} focus:outline-none focus:border-primary`}
            >
                {options.map((opt) => (
                    <option key={opt.value} value={opt.value}>{opt.label}</option>
                ))}
            </NativeSelect>
        </label>
    )
}

export function SearchButton({ onClick }) {
    return (
        <button
            type="button"
            onClick={onClick}
            className="inline-flex h-9 items-center justify-center gap-1.5 rounded-lg border border-transparent bg-linear-to-br from-violet-500 to-primary px-4 text-xs font-bold text-white shadow-sm transition-[filter] hover:brightness-105 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
        >
            <Search size={14} />
            Search
        </button>
    )
}

export function ResetButton({ onClick }) {
    return (
        <button
            type="button"
            onClick={onClick}
            className="inline-flex h-9 items-center gap-1.5 rounded-lg border border-input bg-card px-4 text-xs font-bold text-foreground transition-colors hover:border-primary hover:text-primary"
        >
            Reset
        </button>
    )
}

/**
 * Switch-style toggle for binary IsDeleted filter (iOS-like slider).
 * OFF (gray background, slider left)   = 'active'  (IsDeleted=0, default)
 * ON  (primary background, slider right) = 'deleted' (IsDeleted=1)
 *
 * No inline text — relies on tooltip + aria-label for state context.
 * Backend still understands 'all' from manual URL — we just don't surface it in UI.
 */
export function StatusToggle({ value, onChange }) {
    const isOn = value === 'deleted'
    const tip = isOn
        ? 'Menampilkan data terhapus — klik untuk kembali ke data aktif'
        : 'Menampilkan data aktif — klik untuk lihat data terhapus'

    return (
        <button
            type="button"
            role="switch"
            aria-checked={isOn}
            aria-label={tip}
            title={tip}
            onClick={() => onChange(isOn ? 'active' : 'deleted')}
            className={`inline-flex h-9 cursor-pointer items-center gap-1.5 whitespace-nowrap rounded-full border px-3.5 text-xs font-semibold transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 ${
                isOn
                    ? 'border-danger/50 bg-danger/10 text-danger-text'
                    : 'border-input bg-card text-foreground hover:border-primary hover:text-primary'
            }`}
        >
            <span>IsDeleted</span>
            {isOn && <span aria-hidden="true">✓</span>}
        </button>
    )
}

// ──────────────────────────────────────────────────────────────
// Table primitives (Th, Td, ListTable wrapper)
// ──────────────────────────────────────────────────────────────

// Sort state provided by a page via <ListTable sort={{ sortKey, sortDir, toggleSort }}>;
// Th cells opt in per column with sortId. Resize AND header drag-to-reorder are
// automatic for every ListTable: on mount the auto-layout widths are measured once,
// the table locks to table-fixed with a colgroup, each header edge becomes a drag
// grip, and each header (unless <Th draggable={false}>) can be dragged onto another
// to swap column positions — body cells follow because ListTable re-orders every
// row's cells with the same permutation. Column order persists per pathname.
const ListTableContext = createContext(null)

const permKey = (n) => `listTableColOrder:${window.location.pathname}:${n}`

// Reorder every full-width row's cells (thead + tbody) by the display permutation.
// Rows whose cell count differs (EmptyRow colSpan etc.) pass through untouched.
function applyColOrder(children, perm) {
    if (!perm) return children
    const permuteRow = (row) => {
        if (!isValidElement(row)) return row
        const cells = Children.toArray(row.props?.children)
        if (cells.length !== perm.length) return row
        return cloneElement(row, {}, perm.map((orig) => cells[orig]))
    }
    return Children.map(children, (section) => {
        if (!isValidElement(section)) return section
        if (section.type === 'thead' || section.type === 'tbody') {
            return cloneElement(section, {}, Children.map(section.props.children, permuteRow))
        }
        return section
    })
}

export function ListTable({ minWidth = 'min-w-[760px]', sort = null, children }) {
    const tableRef = useRef(null)
    const [widths, setWidths] = useState(null)     // keyed by ORIGINAL column index
    const [perm, setPerm] = useState(null)         // display position -> original index
    const [resizingIdx, setResizingIdx] = useState(null)
    const [dragIdx, setDragIdx] = useState(null)
    const [dragOverIdx, setDragOverIdx] = useState(null)

    useLayoutEffect(() => {
        if (widths) return
        const ths = tableRef.current?.querySelectorAll('thead th')
        if (!ths?.length) return
        setWidths(Array.from(ths, (th) => th.offsetWidth))
        // Restore the persisted column order once the column count is known.
        try {
            const raw = localStorage.getItem(permKey(ths.length))
            if (raw) {
                const p = JSON.parse(raw)
                if (Array.isArray(p) && p.length === ths.length && new Set(p).size === p.length
                    && p.every((i) => Number.isInteger(i) && i >= 0 && i < p.length)) setPerm(p)
            }
        } catch { /* private mode / corrupt payload */ }
    }, [widths])

    const identity = widths ? widths.map((_, i) => i) : null
    const order = perm ?? identity

    const resizeCol = (displayIdx, w) => setWidths((prev) => {
        if (!prev) return prev
        const orig = (perm ?? prev.map((_, i) => i))[displayIdx]
        if (orig === undefined) return prev
        const next = [...prev]
        next[orig] = Math.max(56, w)
        return next
    })

    const reorderCols = (from, to) => {
        if (from === null || to === null || from === to || !order) return
        const next = [...order]
        const [moved] = next.splice(from, 1)
        next.splice(to, 0, moved)
        setPerm(next)
        try { localStorage.setItem(permKey(order.length), JSON.stringify(next)) } catch { /* ignore */ }
    }

    return (
        <div className="overflow-x-auto rounded-xl pt-4">
            <table ref={tableRef} className={`w-full ${minWidth} border-separate border-spacing-0 [&_tbody_tr:last-child_td]:border-b-0 ${widths ? 'table-fixed' : ''}`}>
                {widths && <colgroup>{order.map((orig) => <col key={orig} style={{ width: widths[orig] }} />)}</colgroup>}
                <ListTableContext.Provider value={{ sort, resizeCol, resizingIdx, setResizingIdx, reorderCols, dragIdx, setDragIdx, dragOverIdx, setDragOverIdx }}>
                    {applyColOrder(children, perm)}
                </ListTableContext.Provider>
            </table>
        </div>
    )
}

export function Th({ children, sortId = null, resizable = true, draggable = true, className = '' }) {
    const ctx = useContext(ListTableContext)
    const thRef = useRef(null)
    const [myIdx, setMyIdx] = useState(null)

    const displayIdx = () => {
        const th = thRef.current
        return th ? Array.prototype.indexOf.call(th.parentNode.children, th) : -1
    }

    const startResize = (e) => {
        e.preventDefault()
        e.stopPropagation()
        const th = thRef.current
        if (!th || !ctx) return
        const idx = displayIdx()
        const startX = e.clientX
        const startW = th.offsetWidth
        setMyIdx(idx)
        ctx.setResizingIdx(idx)
        const move = (ev) => ctx.resizeCol(idx, startW + (ev.clientX - startX))
        const up = () => {
            window.removeEventListener('mousemove', move)
            window.removeEventListener('mouseup', up)
            ctx.setResizingIdx(null)
            setMyIdx(null)
        }
        window.addEventListener('mousemove', move)
        window.addEventListener('mouseup', up)
    }

    const active = ctx?.resizingIdx !== null && ctx?.resizingIdx === myIdx
    const canDrag = Boolean(ctx) && draggable
    const isDragging = canDrag && ctx.dragIdx !== null && ctx.dragIdx === myIdx
    const isDragOver = canDrag && ctx.dragOverIdx !== null && ctx.dragOverIdx === myIdx && ctx.dragIdx !== myIdx
    const dragHandlers = canDrag ? {
        draggable: true,
        onDragStart: () => { const i = displayIdx(); setMyIdx(i); ctx.setDragIdx(i) },
        onDragOver: (e) => { e.preventDefault(); const i = displayIdx(); setMyIdx(i); ctx.setDragOverIdx(i) },
        onDrop: () => { ctx.reorderCols(ctx.dragIdx, displayIdx()); ctx.setDragIdx(null); ctx.setDragOverIdx(null) },
        onDragEnd: () => { ctx.setDragIdx(null); ctx.setDragOverIdx(null) },
        title: 'Drag to reorder · drag right edge to resize',
    } : {}

    return (
        <th ref={thRef} {...dragHandlers} className={`group/col relative bg-secondary/50 text-muted-foreground text-left whitespace-nowrap select-none px-[22px] py-3 text-[11px] font-semibold uppercase tracking-wide first:rounded-l-full first:pl-7 last:rounded-r-full last:pr-5 ${canDrag ? 'cursor-grab active:cursor-grabbing' : ''} ${isDragging ? 'opacity-40' : ''} ${isDragOver ? 'bg-accent !text-primary' : ''} ${className}`}>
            {ctx?.sort && sortId
                ? <SortButton id={sortId} label={children} sortKey={ctx.sort.sortKey} sortDir={ctx.sort.sortDir} onToggle={ctx.sort.toggleSort} />
                : children}
            {resizable && (
                <span
                    onMouseDown={startResize}
                    onClick={(e) => e.stopPropagation()}
                    title="Drag to resize"
                    className={`absolute inset-y-1.5 right-0 w-[5px] cursor-col-resize rounded-full transition-colors ${active ? 'bg-primary' : 'bg-transparent hover:bg-primary/40 group-hover/col:bg-border'}`}
                />
            )}
        </th>
    )
}

export function Td({ children, className = '' }) {
    return (
        <td className={`text-card-foreground whitespace-nowrap overflow-hidden text-ellipsis border-b border-border/60 px-[22px] py-[13px] text-[12px] ${className}`}>
            {children}
        </td>
    )
}

export function TdId({ children, className = '' }) {
    return (
        <td className={`text-primary whitespace-nowrap overflow-hidden text-ellipsis border-b border-border/60 px-[22px] py-[13px] text-[12px] pl-7 tabular-nums font-bold ${className}`}>
            {children}
        </td>
    )
}

export function TdActions({ children }) {
    return (
        <td className="whitespace-nowrap border-b border-border/60 px-[22px] py-[13px] text-[12px] text-right pr-5">
            {children}
        </td>
    )
}

export function Row({ children, className = '', onClick }) {
    return (
        <tr onClick={onClick} className={`transition-colors duration-150 even:bg-secondary/25 hover:bg-secondary/60 ${onClick ? 'cursor-pointer ' : ''}${className}`}>
            {children}
        </tr>
    )
}

export function EmptyRow({ colSpan, children }) {
    return (
        <tr>
            <td colSpan={colSpan} className="text-center text-text-muted italic px-3 py-8 whitespace-normal">
                {children}
            </td>
        </tr>
    )
}

// ──────────────────────────────────────────────────────────────
// YesNoBadge — boolean badge with check/cross icon
// Used for boolean flag columns (e.g. IsLabDiv, IsShow)
// ──────────────────────────────────────────────────────────────

export function YesNoBadge({ value, yesLabel = 'Yes', noLabel = 'No' }) {
    if (value) {
        return (
            <span className="inline-flex items-center gap-1.5 px-2.5 py-0.5 bg-success-bg text-success-text rounded-full text-[11px] font-semibold">
                <Check size={12} />
                {yesLabel}
            </span>
        )
    }
    return (
        <span className="inline-flex items-center gap-1.5 px-2.5 py-0.5 bg-surface-tint text-text-muted rounded-full text-[11px] font-semibold">
            <X size={12} />
            {noLabel}
        </span>
    )
}

// ──────────────────────────────────────────────────────────────
// RowActions — pill-style action buttons (Show/Edit/Delete/Restore)
// ──────────────────────────────────────────────────────────────

export function RowActions({ isDeleted, onShow, onEdit, onDelete, onRestore }) {
    // Ghost icon buttons — same grammar as the Documents list actions (icon-only, so each
    // carries title + aria-label for hover tooltip and screen readers).
    const btn = 'inline-grid size-7 place-items-center rounded-md transition-colors'
    return (
        <span className="inline-flex items-center gap-0.5">
            {onShow && (
                <button type="button" onClick={onShow} title="Show" aria-label="Show"
                    className={`${btn} text-muted-foreground hover:bg-primary/10 hover:text-primary`}>
                    <Eye className="size-3.5" />
                </button>
            )}
            {!isDeleted && onEdit && (
                <button type="button" onClick={onEdit} title="Edit" aria-label="Edit"
                    className={`${btn} text-primary hover:bg-primary/10`}>
                    <Pencil className="size-3.5" />
                </button>
            )}
            {!isDeleted && onDelete && (
                <button type="button" onClick={onDelete} title="Delete" aria-label="Delete"
                    className={`${btn} text-danger-text hover:bg-danger-bg`}>
                    <Trash2 className="size-3.5" />
                </button>
            )}
            {isDeleted && onRestore && (
                <button type="button" onClick={onRestore} title="Restore" aria-label="Restore"
                    className={`${btn} text-success-text hover:bg-success-bg`}>
                    <RotateCcw className="size-3.5" />
                </button>
            )}
        </span>
    )
}

// ──────────────────────────────────────────────────────────────
// ListFooter — pagination + count meta
// ──────────────────────────────────────────────────────────────

// Canonical 3-zone layout (matches @/Components/Table/ListFooter for client-side
// lists): "Showing X to Y of Z items" left · pager centered · right zone free.
export function ListFooter({ paginator, children }) {
    return (
        <div className="grid grid-cols-1 items-center gap-y-2.5 px-1 pt-4 text-xs text-muted-foreground sm:grid-cols-3">
            {paginator ? (
                <span className="tabular-nums">
                    Showing {paginator.from ?? 0} to {paginator.to ?? 0} of {paginator.total ?? 0} items
                </span>
            ) : <span />}
            <div className="flex justify-center">{children}</div>
            <span aria-hidden="true" />
        </div>
    )
}

// ──────────────────────────────────────────────────────────────
// DeleteModal — generic confirmation dialog (shadow-modal token from @theme)
// ──────────────────────────────────────────────────────────────

// `title` / `confirmLabel` default to the delete wording every other caller relies on. They
// exist for actions that are destructive but are NOT a delete — User Login's Revoke blanks the
// credentials and keeps the row, so a dialog headed "Konfirmasi Hapus" would describe the wrong
// outcome to the person clicking it.
/**
 * `confirmTone` and `busy` are optional and default to the delete behaviour every existing
 * caller relies on. They exist because this dialog is also the app's generic "are you sure",
 * and a red button on a non-destructive confirm ("Login sebagai") says the wrong thing.
 * `busy` disables both buttons so a double click cannot fire the action twice — on a
 * non-idempotent POST the second one lands as an error dialog on top of a success.
 */
const CONFIRM_TONES = {
    danger: 'border-transparent bg-danger text-white hover:bg-danger/90',
    warning: 'border-warning-border bg-warning-bg text-warning-text hover:border-warning',
}

export function DeleteModal({ label, message, onCancel, onConfirm, title = 'Konfirmasi Hapus', confirmLabel = 'Hapus', confirmTone = 'danger', busy = false }) {
    return (
        <div
            className="fixed inset-0 z-50 flex items-center justify-center bg-black/40"
            onClick={busy ? undefined : onCancel}
        >
            <div
                className="bg-surface rounded-2xl shadow-modal max-w-md w-full mx-4 border border-border"
                onClick={(e) => e.stopPropagation()}
            >
                <div className="px-5 py-4 border-b border-border">
                    <h2 className="text-sm font-bold text-text-heading">{title}</h2>
                </div>
                <div className="p-5 text-sm text-text-body">
                    {message ?? (
                        <>
                            Yakin hapus <strong className="text-text-heading">{label}</strong>?
                            <br />
                            Data akan disembunyikan tapi bisa di-restore.
                        </>
                    )}
                </div>
                <div className="px-5 py-4 border-t border-border flex justify-end gap-2">
                    <button
                        type="button"
                        onClick={onCancel}
                        disabled={busy}
                        className="h-9 px-4 rounded-lg border border-input bg-card text-sm font-semibold text-foreground transition-colors hover:border-primary hover:text-primary disabled:opacity-60"
                    >
                        Batal
                    </button>
                    <button
                        type="button"
                        onClick={onConfirm}
                        disabled={busy}
                        className={`h-9 px-4 rounded-lg border text-sm font-semibold transition-colors disabled:opacity-60 ${CONFIRM_TONES[confirmTone] ?? CONFIRM_TONES.danger}`}
                    >
                        {confirmLabel}
                    </button>
                </div>
            </div>
        </div>
    )
}

/**
 * List-toolbar responsive recipe — TWO classes that must be used together.
 *
 * The default `flex flex-wrap` fills lines in DOM order, and the ⚙ is always the LAST child,
 * so on a phone it lands on whatever line is left over. Measured 2026-08-20 across 16 list
 * pages: off line 1 on 15 of them, as far down as 226px — the control that rescues a table
 * whose columns do not fit was itself the thing scrolled out of sight.
 *
 * Recipe:
 *   <div className={TOOLBAR_ROW}>
 *     <label …search… />
 *     <button …⚙… className={TOOLBAR_GEAR} />      ← DOM position matters, see below
 *     <div className={TOOLBAR_FILTERS}> …pills… </div>
 *   </div>
 *
 * ⚠️ The ⚙ must sit in the DOM **right after the search box**, not at the end. `order` cannot
 * substitute for that: a flex line is filled in order sequence, and TOOLBAR_FILTERS is
 * `w-full`, which ends line 1 — anything after it is on line 2 no matter what `order` says.
 * `sm:order-last` then sends the ⚙ back to the far right on desktop, and `sm:contents`
 * dissolves the filter wrapper so the desktop row renders exactly as it did before.
 */
export const TOOLBAR_ROW = 'flex flex-wrap items-center gap-2.5';
export const TOOLBAR_GEAR = 'ml-auto grid size-7 shrink-0 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-primary sm:order-last';
export const TOOLBAR_FILTERS = 'flex w-full flex-wrap items-center gap-2.5 sm:contents';
