import type { PlayerCharacter, PlayerIcon } from "@initiative/domain";
import { Check, Eye, Import, Minus, Plus, Users } from "lucide-react";
import {
	type FormEvent,
	type RefObject,
	useEffect,
	useRef,
	useState,
} from "react";
import type { SearchResult } from "../hooks/use-bestiary.js";
import { PLAYER_COLOR_HEX, PLAYER_ICON_MAP } from "./player-icon-map";
import { Button } from "./ui/button.js";
import { Input } from "./ui/input.js";

interface QueuedCreature {
	result: SearchResult;
	count: number;
}

interface ActionBarProps {
	onAddCombatant: (
		name: string,
		opts?: { initiative?: number; ac?: number; maxHp?: number },
	) => void;
	onAddFromBestiary: (result: SearchResult) => void;
	bestiarySearch: (query: string) => SearchResult[];
	bestiaryLoaded: boolean;
	onViewStatBlock?: (result: SearchResult) => void;
	onBulkImport?: () => void;
	bulkImportDisabled?: boolean;
	inputRef?: RefObject<HTMLInputElement | null>;
	playerCharacters?: readonly PlayerCharacter[];
	onAddFromPlayerCharacter?: (pc: PlayerCharacter) => void;
	onManagePlayers?: () => void;
}

function creatureKey(r: SearchResult): string {
	return `${r.source}:${r.name}`;
}

export function ActionBar({
	onAddCombatant,
	onAddFromBestiary,
	bestiarySearch,
	bestiaryLoaded,
	onViewStatBlock,
	onBulkImport,
	bulkImportDisabled,
	inputRef,
	playerCharacters,
	onAddFromPlayerCharacter,
	onManagePlayers,
}: ActionBarProps) {
	const [nameInput, setNameInput] = useState("");
	const [suggestions, setSuggestions] = useState<SearchResult[]>([]);
	const [pcMatches, setPcMatches] = useState<PlayerCharacter[]>([]);
	const [suggestionIndex, setSuggestionIndex] = useState(-1);
	const [queued, setQueued] = useState<QueuedCreature | null>(null);
	const [customInit, setCustomInit] = useState("");
	const [customAc, setCustomAc] = useState("");
	const [customMaxHp, setCustomMaxHp] = useState("");

	// Stat block viewer: separate dropdown
	const [viewerOpen, setViewerOpen] = useState(false);
	const [viewerQuery, setViewerQuery] = useState("");
	const [viewerResults, setViewerResults] = useState<SearchResult[]>([]);
	const [viewerIndex, setViewerIndex] = useState(-1);
	const viewerRef = useRef<HTMLDivElement>(null);
	const viewerInputRef = useRef<HTMLInputElement>(null);

	const clearCustomFields = () => {
		setCustomInit("");
		setCustomAc("");
		setCustomMaxHp("");
	};

	const confirmQueued = () => {
		if (!queued) return;
		for (let i = 0; i < queued.count; i++) {
			onAddFromBestiary(queued.result);
		}
		setQueued(null);
		setNameInput("");
		setSuggestions([]);
		setPcMatches([]);
		setSuggestionIndex(-1);
	};

	const parseNum = (v: string): number | undefined => {
		if (v.trim() === "") return undefined;
		const n = Number(v);
		return Number.isNaN(n) ? undefined : n;
	};

	const handleAdd = (e: FormEvent) => {
		e.preventDefault();
		if (queued) {
			confirmQueued();
			return;
		}
		if (nameInput.trim() === "") return;
		const opts: { initiative?: number; ac?: number; maxHp?: number } = {};
		const init = parseNum(customInit);
		const ac = parseNum(customAc);
		const maxHp = parseNum(customMaxHp);
		if (init !== undefined) opts.initiative = init;
		if (ac !== undefined) opts.ac = ac;
		if (maxHp !== undefined) opts.maxHp = maxHp;
		onAddCombatant(nameInput, Object.keys(opts).length > 0 ? opts : undefined);
		setNameInput("");
		setSuggestions([]);
		setPcMatches([]);
		clearCustomFields();
	};

	const handleNameChange = (value: string) => {
		setNameInput(value);
		setSuggestionIndex(-1);
		let newSuggestions: SearchResult[] = [];
		let newPcMatches: PlayerCharacter[] = [];
		if (value.length >= 2) {
			newSuggestions = bestiarySearch(value);
			setSuggestions(newSuggestions);
			if (playerCharacters && playerCharacters.length > 0) {
				const lower = value.toLowerCase();
				newPcMatches = playerCharacters.filter((pc) =>
					pc.name.toLowerCase().includes(lower),
				);
			}
			setPcMatches(newPcMatches);
		} else {
			setSuggestions([]);
			setPcMatches([]);
		}
		if (newSuggestions.length > 0 || newPcMatches.length > 0) {
			clearCustomFields();
		}
		if (queued) {
			const qKey = creatureKey(queued.result);
			const stillVisible = newSuggestions.some((s) => creatureKey(s) === qKey);
			if (!stillVisible) {
				setQueued(null);
			}
		}
	};

	const handleClickSuggestion = (result: SearchResult) => {
		const key = creatureKey(result);
		if (queued && creatureKey(queued.result) === key) {
			setQueued({ ...queued, count: queued.count + 1 });
		} else {
			setQueued({ result, count: 1 });
		}
	};

	const handleEnter = () => {
		if (queued) {
			confirmQueued();
		} else if (suggestionIndex >= 0) {
			handleClickSuggestion(suggestions[suggestionIndex]);
		}
	};

	const hasSuggestions = suggestions.length > 0 || pcMatches.length > 0;

	const handleKeyDown = (e: React.KeyboardEvent) => {
		if (!hasSuggestions) return;

		if (e.key === "ArrowDown") {
			e.preventDefault();
			setSuggestionIndex((i) => (i < suggestions.length - 1 ? i + 1 : 0));
		} else if (e.key === "ArrowUp") {
			e.preventDefault();
			setSuggestionIndex((i) => (i > 0 ? i - 1 : suggestions.length - 1));
		} else if (e.key === "Enter") {
			e.preventDefault();
			handleEnter();
		} else if (e.key === "Escape") {
			setQueued(null);
			setSuggestionIndex(-1);
			setSuggestions([]);
			setPcMatches([]);
		}
	};

	// Stat block viewer dropdown handlers
	const openViewer = () => {
		setViewerOpen(true);
		setViewerQuery("");
		setViewerResults([]);
		setViewerIndex(-1);
		requestAnimationFrame(() => viewerInputRef.current?.focus());
	};

	const closeViewer = () => {
		setViewerOpen(false);
		setViewerQuery("");
		setViewerResults([]);
		setViewerIndex(-1);
	};

	const handleViewerQueryChange = (value: string) => {
		setViewerQuery(value);
		setViewerIndex(-1);
		if (value.length >= 2) {
			setViewerResults(bestiarySearch(value));
		} else {
			setViewerResults([]);
		}
	};

	const handleViewerSelect = (result: SearchResult) => {
		onViewStatBlock?.(result);
		closeViewer();
	};

	const handleViewerKeyDown = (e: React.KeyboardEvent) => {
		if (e.key === "Escape") {
			closeViewer();
			return;
		}
		if (viewerResults.length === 0) return;

		if (e.key === "ArrowDown") {
			e.preventDefault();
			setViewerIndex((i) => (i < viewerResults.length - 1 ? i + 1 : 0));
		} else if (e.key === "ArrowUp") {
			e.preventDefault();
			setViewerIndex((i) => (i > 0 ? i - 1 : viewerResults.length - 1));
		} else if (e.key === "Enter" && viewerIndex >= 0) {
			e.preventDefault();
			handleViewerSelect(viewerResults[viewerIndex]);
		}
	};

	// Close viewer on outside click
	useEffect(() => {
		if (!viewerOpen) return;
		function handleClickOutside(e: MouseEvent) {
			if (viewerRef.current && !viewerRef.current.contains(e.target as Node)) {
				closeViewer();
			}
		}
		document.addEventListener("mousedown", handleClickOutside);
		return () => document.removeEventListener("mousedown", handleClickOutside);
	}, [viewerOpen]);

	return (
		<div className="flex items-center gap-3 rounded-md border border-border bg-card px-4 py-3">
			<form
				onSubmit={handleAdd}
				className="relative flex flex-1 items-center gap-2"
			>
				<div className="relative flex-1">
					<Input
						ref={inputRef}
						type="text"
						value={nameInput}
						onChange={(e) => handleNameChange(e.target.value)}
						onKeyDown={handleKeyDown}
						placeholder="+ Add combatants"
						className="max-w-xs"
					/>
					{hasSuggestions && (
						<div className="absolute bottom-full z-50 mb-1 w-full max-w-xs rounded-md border border-border bg-card shadow-lg">
							<button
								type="button"
								className="flex w-full items-center gap-1.5 border-b border-border px-3 py-2 text-left text-sm text-accent hover:bg-accent/20"
								onMouseDown={(e) => e.preventDefault()}
								onClick={() => {
									setSuggestions([]);
									setPcMatches([]);
									setQueued(null);
									setSuggestionIndex(-1);
								}}
							>
								<Plus className="h-3.5 w-3.5" />
								<span className="flex-1">Add "{nameInput}" as custom</span>
								<kbd className="rounded border border-border px-1.5 py-0.5 text-xs text-muted-foreground">
									Esc
								</kbd>
							</button>
							<div className="max-h-48 overflow-y-auto py-1">
								{pcMatches.length > 0 && (
									<>
										<div className="px-3 py-1 text-xs font-medium text-muted-foreground">
											Players
										</div>
										<ul>
											{pcMatches.map((pc) => {
												const PcIcon = PLAYER_ICON_MAP[pc.icon as PlayerIcon];
												const pcColor =
													PLAYER_COLOR_HEX[
														pc.color as keyof typeof PLAYER_COLOR_HEX
													];
												return (
													<li key={pc.id}>
														<button
															type="button"
															className="flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm text-foreground hover:bg-hover-neutral-bg"
															onMouseDown={(e) => e.preventDefault()}
															onClick={() => {
																onAddFromPlayerCharacter?.(pc);
																setNameInput("");
																setSuggestions([]);
																setPcMatches([]);
															}}
														>
															{PcIcon && (
																<PcIcon size={14} style={{ color: pcColor }} />
															)}
															<span className="flex-1 truncate">{pc.name}</span>
															<span className="text-xs text-muted-foreground">
																Player
															</span>
														</button>
													</li>
												);
											})}
										</ul>
									</>
								)}
								{suggestions.length > 0 && (
									<ul>
										{suggestions.map((result, i) => {
											const key = creatureKey(result);
											const isQueued =
												queued !== null && creatureKey(queued.result) === key;
											return (
												<li key={key}>
													<button
														type="button"
														className={`flex w-full items-center justify-between px-3 py-1.5 text-left text-sm ${
															isQueued
																? "bg-accent/30 text-foreground"
																: i === suggestionIndex
																	? "bg-accent/20 text-foreground"
																	: "text-foreground hover:bg-hover-neutral-bg"
														}`}
														onMouseDown={(e) => e.preventDefault()}
														onClick={() => handleClickSuggestion(result)}
														onMouseEnter={() => setSuggestionIndex(i)}
													>
														<span>{result.name}</span>
														<span className="flex items-center gap-1 text-xs text-muted-foreground">
															{isQueued ? (
																<>
																	<button
																		type="button"
																		className="rounded p-0.5 text-foreground hover:bg-accent/40"
																		onMouseDown={(e) => e.preventDefault()}
																		onClick={(e) => {
																			e.stopPropagation();
																			if (queued.count <= 1) {
																				setQueued(null);
																			} else {
																				setQueued({
																					...queued,
																					count: queued.count - 1,
																				});
																			}
																		}}
																	>
																		<Minus className="h-3 w-3" />
																	</button>
																	<span className="min-w-5 rounded-full bg-accent px-1.5 py-0.5 text-center font-medium text-foreground">
																		{queued.count}
																	</span>
																	<button
																		type="button"
																		className="rounded p-0.5 text-foreground hover:bg-accent/40"
																		onMouseDown={(e) => e.preventDefault()}
																		onClick={(e) => {
																			e.stopPropagation();
																			setQueued({
																				...queued,
																				count: queued.count + 1,
																			});
																		}}
																	>
																		<Plus className="h-3 w-3" />
																	</button>
																	<button
																		type="button"
																		className="ml-0.5 rounded p-0.5 text-foreground hover:bg-accent/40"
																		onMouseDown={(e) => e.preventDefault()}
																		onClick={(e) => {
																			e.stopPropagation();
																			confirmQueued();
																		}}
																	>
																		<Check className="h-3.5 w-3.5" />
																	</button>
																</>
															) : (
																result.sourceDisplayName
															)}
														</span>
													</button>
												</li>
											);
										})}
									</ul>
								)}
							</div>
						</div>
					)}
				</div>
				{nameInput.length >= 2 && !hasSuggestions && (
					<div className="flex items-center gap-2">
						<Input
							type="text"
							inputMode="numeric"
							value={customInit}
							onChange={(e) => setCustomInit(e.target.value)}
							placeholder="Init"
							className="w-16 text-center"
						/>
						<Input
							type="text"
							inputMode="numeric"
							value={customAc}
							onChange={(e) => setCustomAc(e.target.value)}
							placeholder="AC"
							className="w-16 text-center"
						/>
						<Input
							type="text"
							inputMode="numeric"
							value={customMaxHp}
							onChange={(e) => setCustomMaxHp(e.target.value)}
							placeholder="MaxHP"
							className="w-18 text-center"
						/>
					</div>
				)}
				<Button type="submit" size="sm">
					Add
				</Button>
				<div className="flex items-center gap-0">
					{onManagePlayers && (
						<Button
							type="button"
							size="icon"
							variant="ghost"
							className="text-muted-foreground hover:text-hover-neutral"
							onClick={onManagePlayers}
							title="Player characters"
							aria-label="Player characters"
						>
							<Users className="h-5 w-5" />
						</Button>
					)}
					{bestiaryLoaded && onViewStatBlock && (
						<div ref={viewerRef} className="relative">
							<Button
								type="button"
								size="icon"
								variant="ghost"
								className="text-muted-foreground hover:text-hover-neutral"
								onClick={() => (viewerOpen ? closeViewer() : openViewer())}
								title="Browse stat blocks"
								aria-label="Browse stat blocks"
							>
								<Eye className="h-5 w-5" />
							</Button>
							{viewerOpen && (
								<div className="absolute bottom-full right-0 z-50 mb-1 w-64 rounded-md border border-border bg-card shadow-lg">
									<div className="p-2">
										<Input
											ref={viewerInputRef}
											type="text"
											value={viewerQuery}
											onChange={(e) => handleViewerQueryChange(e.target.value)}
											onKeyDown={handleViewerKeyDown}
											placeholder="Search stat blocks..."
											className="w-full"
										/>
									</div>
									{viewerResults.length > 0 && (
										<ul className="max-h-48 overflow-y-auto border-t border-border py-1">
											{viewerResults.map((result, i) => (
												<li key={creatureKey(result)}>
													<button
														type="button"
														className={`flex w-full items-center justify-between px-3 py-1.5 text-left text-sm ${
															i === viewerIndex
																? "bg-accent/20 text-foreground"
																: "text-foreground hover:bg-hover-neutral-bg"
														}`}
														onClick={() => handleViewerSelect(result)}
														onMouseEnter={() => setViewerIndex(i)}
													>
														<span>{result.name}</span>
														<span className="text-xs text-muted-foreground">
															{result.sourceDisplayName}
														</span>
													</button>
												</li>
											))}
										</ul>
									)}
									{viewerQuery.length >= 2 && viewerResults.length === 0 && (
										<div className="border-t border-border px-3 py-2 text-sm text-muted-foreground">
											No creatures found
										</div>
									)}
								</div>
							)}
						</div>
					)}
					{bestiaryLoaded && onBulkImport && (
						<Button
							type="button"
							size="icon"
							variant="ghost"
							className="text-muted-foreground hover:text-hover-neutral"
							onClick={onBulkImport}
							disabled={bulkImportDisabled}
							title="Bulk import"
							aria-label="Bulk import"
						>
							<Import className="h-5 w-5" />
						</Button>
					)}
				</div>
			</form>
		</div>
	);
}
