import type { ColorInput } from "bun";
import type { Accessor } from "solid-js";
import { useClock } from "../animation/clock.ts";

/**
 * Named glyph-frame presets for {@link Spinner}. Each is the literal sequence
 * of frames cycled in order; pass one to the `glyphs` prop, or supply your own
 * `readonly string[]`.
 *
 * - `sparkle` — the default: a sparkle that grows then shrinks for a smooth
 *   bounce (uses `*` instead of `✳` off macOS for wider font coverage).
 * - `line` — the classic `-\|/` ASCII spinner.
 * - `dots` — a braille spinner (`⠋⠙⠹…`).
 * - `circle` — a rotating quarter-filled circle (`◐◓◑◒`).
 */
export const SPINNER_GLYPHS = {
	sparkle:
		process.platform === "darwin"
			? (["·", "✢", "✳", "✶", "✻", "✽", "✻", "✶", "✳", "✢"] as const)
			: (["·", "✢", "*", "✶", "✻", "✽", "✻", "✶", "*", "✢"] as const),
	line: ["-", "\\", "|", "/"] as const,
	dots: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] as const,
	circle: ["◐", "◓", "◑", "◒"] as const,
} satisfies Record<string, readonly string[]>;

const DEFAULT_BASE = 0x58a6ff; // #58a6ff blue

export type SpinnerProps = {
	/**
	 * Glyph frames cycled in order. Pass a {@link SPINNER_GLYPHS} preset or your
	 * own `readonly string[]`. Default {@link SPINNER_GLYPHS.sparkle}.
	 */
	glyphs?: readonly string[];
	/** Milliseconds per glyph frame. Default 120. */
	glyphInterval?: number;
	/**
	 * Glyph color. Inside an `Animated`/`Shimmer`/… region the group's paint
	 * filter recolors the glyph instead. Default blue (#58a6ff).
	 */
	color?: ColorInput;
	/** Expose the internal clock for external consumers (e.g. useStalled). */
	clockRef?: (time: Accessor<number>) => void;
};

/**
 * A bare animated spinner glyph — just the bouncing glyph in `color`. It needs
 * no animation awareness: dropped inside an `Animated`/`Shimmer`/`Wave`/`Pulse`
 * region, the group's paint filter recolors the glyph along with the text
 * beside it, so an effect sweeps across the spinner and its label as one.
 *
 * Stateful coloring (e.g. fading toward red as a stream stalls) belongs to the
 * caller, not here — wrap the spinner in a `Shimmer`/`Animated` whose colors
 * animate (see the `stream-spinner` demo), and the paint filter recolors the
 * glyph along with everything else in the region.
 */
export function Spinner(props: SpinnerProps) {
	const time = useClock(50);
	if (props.clockRef) props.clockRef(time);

	const frames = () => props.glyphs ?? SPINNER_GLYPHS.sparkle;
	const interval = () => props.glyphInterval ?? 120;
	const glyph = () => {
		const f = frames();
		return f[Math.floor(time() / interval()) % f.length] ?? f[0];
	};

	return (
		<text bold color={props.color ?? DEFAULT_BASE}>
			{glyph()}
		</text>
	);
}
