import { Check, Search, X } from 'lucide-react';
import * as React from 'react';
import { cn } from '@/lib/utils';

/**
 * One member of staff who can hold a ticket.
 */
export type StaffOption = {
    value: number;
    label: string;
    /** Their role, shown beside the name and searched along with it. */
    role?: string | null;
};

type StaffPickerProps = {
    id: string;
    /** The field name posted to the server; every choice goes in as name[]. */
    name: string;
    options: StaffOption[];
    /** Everyone currently holding the ticket, in the order they were added. */
    value: number[];
    onChange: (value: number[]) => void;
    disabled?: boolean;
    placeholder?: string;
};

/**
 * Who in the office is holding a ticket — however many of them that is.
 *
 * Support work is split across people: one takes the call, another chases the
 * crew, a third sorts out the invoice query on the same job. A single select
 * could only record the first of them, so this one keeps a set: the people
 * chosen are listed as chips that can be taken off, and the rest of the staff
 * list sits below with a filter for finding somebody by name or role.
 *
 * The form posts one hidden input per person, so the server reads the whole set
 * rather than having to work out what changed.
 */
export function StaffPicker({
    id,
    name,
    options,
    value,
    onChange,
    disabled = false,
    placeholder = 'Search staff and support',
}: StaffPickerProps) {
    const [search, setSearch] = React.useState('');

    const chosen = value
        .map((memberId) => options.find((option) => option.value === memberId))
        .filter((option): option is StaffOption => option !== undefined);

    const term = search.trim().toLowerCase();

    // The people already on the ticket stay visible while filtering, so they
    // can always be taken off again.
    const listed = options.filter(
        (option) =>
            value.includes(option.value) ||
            term === '' ||
            option.label.toLowerCase().includes(term) ||
            (option.role ?? '').toLowerCase().includes(term),
    );

    const toggle = (memberId: number) => {
        onChange(
            value.includes(memberId)
                ? value.filter((chosenId) => chosenId !== memberId)
                : [...value, memberId],
        );
    };

    return (
        <div className={cn('flex flex-col gap-2', disabled && 'opacity-60')}>
            {/* What the form actually posts: one entry for every person. */}
            {value.map((memberId) => (
                <input
                    key={memberId}
                    type="hidden"
                    name={`${name}[]`}
                    value={memberId}
                />
            ))}

            {chosen.length === 0 ? (
                <p className="text-xs text-muted-foreground">
                    Nobody in the office is holding this yet.
                </p>
            ) : (
                <ul className="flex flex-wrap gap-1.5" aria-label="Assigned to">
                    {chosen.map((member) => (
                        <li key={member.value}>
                            <span className="inline-flex items-center gap-1 rounded-full border border-hairline bg-accent/40 py-0.5 pr-1 pl-2.5 text-xs">
                                <span className="font-medium">
                                    {member.label}
                                </span>
                                {member.role ? (
                                    <span className="text-muted-foreground">
                                        {member.role}
                                    </span>
                                ) : null}
                                <button
                                    type="button"
                                    disabled={disabled}
                                    onClick={() => toggle(member.value)}
                                    className="rounded-full p-0.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:cursor-not-allowed"
                                    aria-label={`Take ${member.label} off this ticket`}
                                >
                                    <X className="size-3" />
                                </button>
                            </span>
                        </li>
                    ))}
                </ul>
            )}

            <div className="relative">
                <Search
                    aria-hidden
                    className="pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2 text-muted-foreground"
                />
                <input
                    id={id}
                    type="text"
                    autoComplete="off"
                    spellCheck={false}
                    disabled={disabled}
                    value={search}
                    onChange={(event) => setSearch(event.target.value)}
                    placeholder={placeholder}
                    className={cn(
                        'h-9 w-full min-w-0 rounded-md border border-input bg-transparent py-1 pr-3 pl-8 text-sm shadow-xs transition-[color,box-shadow] outline-none dark:bg-input/30',
                        'placeholder:text-muted-foreground',
                        'focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50',
                        'disabled:cursor-not-allowed disabled:opacity-50',
                    )}
                />
            </div>

            <ul
                className="max-h-52 divide-y divide-hairline overflow-y-auto rounded-md border border-input"
                aria-label="Staff who can hold this ticket"
            >
                {listed.length === 0 ? (
                    <li className="px-3 py-2 text-sm text-muted-foreground">
                        No one matches that name.
                    </li>
                ) : (
                    listed.map((member) => {
                        const checked = value.includes(member.value);

                        return (
                            <li key={member.value}>
                                <button
                                    type="button"
                                    role="checkbox"
                                    aria-checked={checked}
                                    disabled={disabled}
                                    onClick={() => toggle(member.value)}
                                    className={cn(
                                        'flex w-full items-center gap-2 px-3 py-2 text-left text-sm transition-colors',
                                        'hover:bg-accent/50 disabled:cursor-not-allowed',
                                        checked && 'bg-accent/30',
                                    )}
                                >
                                    <span
                                        aria-hidden
                                        className={cn(
                                            'flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input',
                                            checked &&
                                                'border-primary bg-primary text-primary-foreground',
                                        )}
                                    >
                                        {checked ? (
                                            <Check className="size-3" />
                                        ) : null}
                                    </span>
                                    <span className="min-w-0 flex-1 truncate">
                                        {member.label}
                                    </span>
                                    {member.role ? (
                                        <span className="shrink-0 text-xs text-muted-foreground">
                                            {member.role}
                                        </span>
                                    ) : null}
                                </button>
                            </li>
                        );
                    })
                )}
            </ul>
        </div>
    );
}
