import type { ColorInput } from "bun";
import type { Element as JSXElement } from "solid-js";
import { createSignal, For } from "solid-js";
import { useInput } from "../input/hooks.ts";

export type SelectProps<T> = {
	/** The rows to choose between. */
	items: readonly T[];
	/**
	 * Render a row's label. Receives the item, whether it's highlighted, and its
	 * index. Default renders `String(item)` (bold + `highlightColor` when
	 * highlighted). Supply this to render richer rows; you then own their styling.
	 */
	children?: (item: T, highlighted: boolean, index: number) => JSXElement;
	/**
	 * Highlighted index. Pass it to drive the highlight yourself (controlled);
	 * omit it to let `Select` track the highlight internally (uncontrolled).
	 */
	index?: number;
	/** Fires when the highlight moves (↑/↓, `k`/`j`, or hover). */
	onHighlight?: (item: T, index: number) => void;
	/** Fires when a row is committed (Enter or click). */
	onSelect?: (item: T, index: number) => void;
	/** Only handle keys while true. Default true. */
	focused?: boolean;
	/** Glyph in the gutter of the highlighted row. Default `❯`. */
	indicator?: string;
	/** Color of the highlighted row + its indicator. Default `#d77757`. */
	highlightColor?: ColorInput;
	/** Color of the non-highlighted rows. */
	color?: ColorInput;
};

/**
 * A keyboard- and mouse-navigable list. `↑`/`↓` (or `k`/`j`) move the
 * highlight, Enter commits it, and — when the screen has `mouse: true` — hover
 * highlights a row and a click commits it. Each row is `indicator + label`, the
 * indicator marking the current row in the gutter so labels stay aligned.
 *
 * Uncontrolled by default (it tracks the highlight itself); pass `index` to
 * drive the highlight from the parent. `onHighlight` fires as the highlight
 * moves, `onSelect` when a row is committed.
 *
 * ```tsx
 * <Select
 *   items={["Build", "Test", "Deploy"]}
 *   onSelect={(item) => run(item)}
 * />
 * ```
 */
export function Select<T>(props: SelectProps<T>) {
	const [internal, setInternal] = createSignal(props.index ?? 0);
	const current = () => props.index ?? internal();
	const indicator = () => props.indicator ?? "❯";
	const focused = () => props.focused ?? true;

	const highlight = (i: number) => {
		setInternal(i);
		const item = props.items[i];
		if (item !== undefined) props.onHighlight?.(item, i);
	};

	const move = (delta: number) => {
		const n = props.items.length;
		if (n === 0) return;
		highlight((current() + delta + n) % n);
	};

	const commit = (i: number) => {
		const item = props.items[i];
		if (item !== undefined) props.onSelect?.(item, i);
	};

	useInput((key) => {
		if (!focused() || props.items.length === 0) return;
		if (key.name === "up" || key.name === "k") move(-1);
		else if (key.name === "down" || key.name === "j") move(1);
		else if (key.name === "return") commit(current());
	});

	return (
		<box flexDirection="column">
			<For each={props.items}>
				{(item, i) => {
					const highlighted = () => i() === current();
					return (
						<box
							flexDirection="row"
							onClick={() => {
								setInternal(i());
								commit(i());
							}}
							onMouseEnter={() => highlight(i())}
						>
							<text bold color={props.highlightColor ?? "#d77757"}>
								{highlighted() ? `${indicator()} ` : "  "}
							</text>
							{props.children ? (
								props.children(item, highlighted(), i())
							) : (
								<text
									bold={highlighted()}
									color={
										highlighted()
											? (props.highlightColor ?? "#d77757")
											: props.color
									}
								>
									{String(item)}
								</text>
							)}
						</box>
					);
				}}
			</For>
		</box>
	);
}
