import type { CombatantId } from "@initiative/domain";
import { type FormEvent, useCallback, useRef, useState } from "react";
import { useEncounter } from "./hooks/use-encounter";

function formatEvent(e: ReturnType<typeof useEncounter>["events"][number]) {
	switch (e.type) {
		case "TurnAdvanced":
			return `Turn: ${e.previousCombatantId} → ${e.newCombatantId} (round ${e.roundNumber})`;
		case "RoundAdvanced":
			return `Round advanced to ${e.newRoundNumber}`;
		case "CombatantAdded":
			return `Added combatant: ${e.name}`;
		case "CombatantRemoved":
			return `Removed combatant: ${e.name}`;
		case "CombatantUpdated":
			return `Renamed combatant: ${e.oldName} → ${e.newName}`;
		case "InitiativeSet":
			return `Initiative: ${e.combatantId} ${e.previousValue ?? "unset"} → ${e.newValue ?? "unset"}`;
		case "MaxHpSet":
			return `Max HP: ${e.combatantId} ${e.previousMaxHp ?? "unset"} → ${e.newMaxHp ?? "unset"}`;
		case "CurrentHpAdjusted":
			return `HP: ${e.combatantId} ${e.previousHp} → ${e.newHp} (${e.delta > 0 ? "+" : ""}${e.delta})`;
	}
}

function EditableName({
	name,
	combatantId,
	isActive,
	onRename,
}: {
	name: string;
	combatantId: CombatantId;
	isActive: boolean;
	onRename: (id: CombatantId, newName: string) => void;
}) {
	const [editing, setEditing] = useState(false);
	const [draft, setDraft] = useState(name);
	const inputRef = useRef<HTMLInputElement>(null);

	const commit = useCallback(() => {
		const trimmed = draft.trim();
		if (trimmed !== "" && trimmed !== name) {
			onRename(combatantId, trimmed);
		}
		setEditing(false);
	}, [draft, name, combatantId, onRename]);

	const startEditing = useCallback(() => {
		setDraft(name);
		setEditing(true);
		requestAnimationFrame(() => inputRef.current?.select());
	}, [name]);

	if (editing) {
		return (
			<input
				ref={inputRef}
				type="text"
				value={draft}
				onChange={(e) => setDraft(e.target.value)}
				onBlur={commit}
				onKeyDown={(e) => {
					if (e.key === "Enter") commit();
					if (e.key === "Escape") setEditing(false);
				}}
			/>
		);
	}

	return (
		<button type="button" onClick={startEditing}>
			{isActive ? `▶ ${name}` : name}
		</button>
	);
}

function MaxHpInput({
	maxHp,
	onCommit,
}: {
	maxHp: number | undefined;
	onCommit: (value: number | undefined) => void;
}) {
	const [draft, setDraft] = useState(maxHp?.toString() ?? "");
	const prev = useRef(maxHp);

	// Sync draft when domain value changes externally (e.g. from another source)
	if (maxHp !== prev.current) {
		prev.current = maxHp;
		setDraft(maxHp?.toString() ?? "");
	}

	const commit = useCallback(() => {
		if (draft === "") {
			onCommit(undefined);
			return;
		}
		const n = Number.parseInt(draft, 10);
		if (!Number.isNaN(n) && n >= 1) {
			onCommit(n);
		} else {
			// Revert invalid input
			setDraft(maxHp?.toString() ?? "");
		}
	}, [draft, maxHp, onCommit]);

	return (
		<input
			type="number"
			min={1}
			value={draft}
			placeholder="Max HP"
			style={{ width: "5em" }}
			onChange={(e) => setDraft(e.target.value)}
			onBlur={commit}
			onKeyDown={(e) => {
				if (e.key === "Enter") commit();
			}}
		/>
	);
}

function CurrentHpInput({
	currentHp,
	maxHp,
	onCommit,
}: {
	currentHp: number | undefined;
	maxHp: number | undefined;
	onCommit: (value: number) => void;
}) {
	const [draft, setDraft] = useState(currentHp?.toString() ?? "");
	const prev = useRef(currentHp);

	if (currentHp !== prev.current) {
		prev.current = currentHp;
		setDraft(currentHp?.toString() ?? "");
	}

	const commit = useCallback(() => {
		if (draft === "" || currentHp === undefined) return;
		const n = Number.parseInt(draft, 10);
		if (!Number.isNaN(n)) {
			onCommit(n);
		} else {
			setDraft(currentHp.toString());
		}
	}, [draft, currentHp, onCommit]);

	return (
		<input
			type="number"
			min={0}
			max={maxHp}
			value={draft}
			placeholder="HP"
			disabled={maxHp === undefined}
			style={{ width: "4em" }}
			onChange={(e) => setDraft(e.target.value)}
			onBlur={commit}
			onKeyDown={(e) => {
				if (e.key === "Enter") commit();
			}}
		/>
	);
}

export function App() {
	const {
		encounter,
		events,
		advanceTurn,
		addCombatant,
		removeCombatant,
		editCombatant,
		setInitiative,
		setHp,
		adjustHp,
	} = useEncounter();
	const activeCombatant = encounter.combatants[encounter.activeIndex];
	const [nameInput, setNameInput] = useState("");

	const handleAdd = (e: FormEvent) => {
		e.preventDefault();
		if (nameInput.trim() === "") return;
		addCombatant(nameInput);
		setNameInput("");
	};

	return (
		<div>
			<h1>Initiative Tracker</h1>

			{activeCombatant && (
				<p>
					Round {encounter.roundNumber} — Current: {activeCombatant.name}
				</p>
			)}

			<ul>
				{encounter.combatants.map((c, i) => (
					<li key={c.id}>
						<EditableName
							name={c.name}
							combatantId={c.id}
							isActive={i === encounter.activeIndex}
							onRename={editCombatant}
						/>{" "}
						<input
							type="number"
							value={c.initiative ?? ""}
							placeholder="Init"
							style={{ width: "4em" }}
							onChange={(e) => {
								const raw = e.target.value;
								if (raw === "") {
									setInitiative(c.id, undefined);
								} else {
									const n = Number.parseInt(raw, 10);
									if (!Number.isNaN(n)) {
										setInitiative(c.id, n);
									}
								}
							}}
						/>{" "}
						{c.maxHp !== undefined && c.currentHp !== undefined && (
							<button type="button" onClick={() => adjustHp(c.id, -1)}>
								-
							</button>
						)}
						<CurrentHpInput
							currentHp={c.currentHp}
							maxHp={c.maxHp}
							onCommit={(value) => {
								if (c.currentHp === undefined) return;
								const delta = value - c.currentHp;
								if (delta !== 0) adjustHp(c.id, delta);
							}}
						/>
						{c.maxHp !== undefined && <span>/</span>}
						{c.maxHp !== undefined && c.currentHp !== undefined && (
							<button type="button" onClick={() => adjustHp(c.id, 1)}>
								+
							</button>
						)}{" "}
						<MaxHpInput maxHp={c.maxHp} onCommit={(v) => setHp(c.id, v)} />{" "}
						<button type="button" onClick={() => removeCombatant(c.id)}>
							Remove
						</button>
					</li>
				))}
			</ul>

			<form onSubmit={handleAdd}>
				<input
					type="text"
					value={nameInput}
					onChange={(e) => setNameInput(e.target.value)}
					placeholder="Combatant name"
				/>
				<button type="submit">Add Combatant</button>
			</form>

			<button type="button" onClick={advanceTurn}>
				Next Turn
			</button>

			{events.length > 0 && (
				<div>
					<h2>Events</h2>
					<ul>
						{events.map((e, i) => (
							<li key={`${e.type}-${i}`}>{formatEvent(e)}</li>
						))}
					</ul>
				</div>
			)}
		</div>
	);
}
