// ToggleSwitch is a reusable on/off switch styled with Tailwind. It renders a
// real <button role="switch"> so it stays keyboard- and screen-reader-friendly,
// with an optional inline label/description to its right.
//
// Props accept either plain values or zero-arg accessors (the SegmentedButtons
// convention), so callers can pass a signal directly: checked={mySignal}.

type Reactive<T> = T | (() => T);

interface ToggleSwitchProps {
    checked: Reactive<boolean>;
    onchange: (next: boolean) => void;
    label?: Reactive<string>;
    description?: Reactive<string>;
    disabled?: Reactive<boolean>;
    class?: string;
}

const resolve = <T,>(v: Reactive<T>): T => (typeof v === "function" ? (v as () => T)() : v);

export function ToggleSwitch(props: ToggleSwitchProps) {
    const isChecked = () => !!resolve(props.checked);
    const isDisabled = () => !!resolve(props.disabled);

    const toggle = () => {
        if (isDisabled()) return;
        props.onchange(!isChecked());
    };

    const trackCls = () =>
        "relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors focus-visible:outline-2 focus-visible:outline-primary focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-50 "
        + (isChecked() ? "bg-primary" : "bg-neutral-300");

    // Track is w-9 (36px) with a w-4 (16px) knob, so a symmetric 2px gap means
    // the knob sits at 2px (translate-x-0.5) when off and 36-16-2=18px when on.
    const knobCls = () =>
        "inline-block h-4 w-4 transform rounded-full bg-white shadow-sm transition-transform "
        + (isChecked() ? "translate-x-[18px]" : "translate-x-0.5");

    const hasText = () => props.label !== undefined || props.description !== undefined;

    return <div class={"flex items-center gap-2 " + (props.class || "")}>
        <button
            type="button"
            role="switch"
            aria-checked={isChecked() ? "true" : "false"}
            disabled={isDisabled()}
            onclick={(_e: MouseEvent) => toggle()}
            class={trackCls()}
        >
            <span class={knobCls()}></span>
        </button>
        {hasText() && <div class="flex flex-col leading-tight">
            {props.label !== undefined && <span
                class={"text-sm select-none " + (isDisabled() ? "text-neutral-400" : "text-neutral-800")}
                onclick={(_e: MouseEvent) => toggle()}
            >{resolve(props.label)}</span>}
            {props.description !== undefined && <span class="text-xs text-neutral-500">{resolve(props.description)}</span>}
        </div>}
    </div>;
}
