"use client";

import { useMemo, useState } from "react";

/*
 * A two-pane picker: everything available on the left, everything chosen on the
 * right, moved across with the four buttons between them.
 *
 * The panes are divs rather than <select multiple>. A native multi-select draws
 * its selected rows with the platform's own highlight colour, which cannot be
 * restyled reliably across browsers — on a dark ground it renders as a pale
 * band that belongs to no palette. Rebuilding the widget is the only way to
 * keep it inside the token system.
 */

export interface DualItem {
    id: string;
    label: string;
    hint?: string;
}

function Pane({
    items,
    marked,
    onToggle,
    onCommit,
    empty,
}: {
    items: DualItem[];
    marked: string[];
    onToggle: (id: string, additive: boolean) => void;
    onCommit: (id: string) => void;
    empty: string;
}) {
    return (
        <div className="h-56 overflow-y-auto rounded-lg border border-border bg-surface-2" role="listbox" aria-multiselectable>
            {items.length === 0 ? (
                <p className="px-3 py-2 text-xs text-text-tertiary">{empty}</p>
            ) : (
                items.map((it) => {
                    const on = marked.includes(it.id);
                    return (
                        <button
                            key={it.id}
                            type="button"
                            role="option"
                            aria-selected={on}
                            onClick={(e) => onToggle(it.id, e.ctrlKey || e.metaKey || e.shiftKey)}
                            onDoubleClick={() => onCommit(it.id)}
                            className={`flex w-full items-baseline gap-2 px-3 py-1.5 text-left text-sm transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-accent ${
                                on ? "bg-accent text-accent-ink" : "text-text-primary hover:bg-surface"
                            }`}
                        >
                            <span className="truncate">{it.label}</span>
                            {it.hint && <span className={`truncate font-mono text-[11px] ${on ? "opacity-70" : "text-text-tertiary"}`}>{it.hint}</span>}
                        </button>
                    );
                })
            )}
        </div>
    );
}

function MoveButton({ children, onClick, disabled, label }: { children: React.ReactNode; onClick: () => void; disabled: boolean; label: string }) {
    return (
        <button
            type="button"
            onClick={onClick}
            disabled={disabled}
            aria-label={label}
            title={label}
            className="rounded-lg border border-border bg-surface-2 px-3 py-1.5 text-sm text-text-secondary transition-colors hover:border-accent/40 hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-accent disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:border-border disabled:hover:text-text-secondary"
        >
            {children}
        </button>
    );
}

export function DualListBox({
    items,
    selected,
    onChange,
    availableLabel = "Available",
    selectedLabel = "Selected",
    emptyAvailable = "Nothing left to add.",
    emptySelected = "Nothing selected.",
}: {
    items: DualItem[];
    selected: string[];
    onChange: (next: string[]) => void;
    availableLabel?: string;
    selectedLabel?: string;
    emptyAvailable?: string;
    emptySelected?: string;
}) {
    // Which rows are highlighted in each pane, not which are chosen. Highlight
    // is transient and per-pane; membership is the `selected` prop.
    const [markedLeft, setMarkedLeft] = useState<string[]>([]);
    const [markedRight, setMarkedRight] = useState<string[]>([]);

    const byLabel = (a: DualItem, b: DualItem) => a.label.localeCompare(b.label);
    const available = useMemo(() => items.filter((i) => !selected.includes(i.id)).sort(byLabel), [items, selected]);
    // Ordered by the same rule as the left pane rather than by the order things
    // were clicked, so a workflow's targets read the same way every time.
    const chosen = useMemo(() => items.filter((i) => selected.includes(i.id)).sort(byLabel), [items, selected]);

    const mark = (setter: typeof setMarkedLeft) => (id: string, additive: boolean) =>
        setter((m) => (additive ? (m.includes(id) ? m.filter((x) => x !== id) : [...m, id]) : m.length === 1 && m[0] === id ? [] : [id]));

    const add = (ids: string[]) => {
        if (ids.length === 0) return;
        onChange([...selected, ...ids.filter((id) => !selected.includes(id))]);
        setMarkedLeft([]);
    };

    const remove = (ids: string[]) => {
        if (ids.length === 0) return;
        onChange(selected.filter((id) => !ids.includes(id)));
        setMarkedRight([]);
    };

    return (
        <div className="grid grid-cols-[1fr_auto_1fr] items-start gap-3">
            <div>
                <p className="mb-1 text-[11px] uppercase tracking-wide text-text-tertiary">{availableLabel}</p>
                <Pane items={available} marked={markedLeft} onToggle={mark(setMarkedLeft)} onCommit={(id) => add([id])} empty={emptyAvailable} />
            </div>

            <div className="flex flex-col gap-2 pt-6">
                <MoveButton label={`Add all to ${selectedLabel}`} disabled={available.length === 0} onClick={() => add(available.map((i) => i.id))}>
                    »
                </MoveButton>
                <MoveButton label={`Add to ${selectedLabel}`} disabled={markedLeft.length === 0} onClick={() => add(markedLeft)}>
                    ›
                </MoveButton>
                <MoveButton label={`Remove from ${selectedLabel}`} disabled={markedRight.length === 0} onClick={() => remove(markedRight)}>
                    ‹
                </MoveButton>
                <MoveButton label={`Remove all from ${selectedLabel}`} disabled={chosen.length === 0} onClick={() => remove(chosen.map((i) => i.id))}>
                    «
                </MoveButton>
            </div>

            <div>
                <p className="mb-1 text-[11px] uppercase tracking-wide text-text-tertiary">
                    {selectedLabel} · {chosen.length}
                </p>
                <Pane items={chosen} marked={markedRight} onToggle={mark(setMarkedRight)} onCommit={(id) => remove([id])} empty={emptySelected} />
            </div>
        </div>
    );
}
