import type { PlayerCharacter, PlayerIcon } from "@initiative/domain";
import {
	Check,
	Eye,
	EyeOff,
	Import,
	Library,
	Minus,
	Plus,
	Users,
} from "lucide-react";
import { type FormEvent, type RefObject, useState } from "react";
import type { SearchResult } from "../hooks/use-bestiary.js";
import { cn } from "../lib/utils.js";
import { D20Icon } from "./d20-icon.js";
import { PLAYER_COLOR_HEX, PLAYER_ICON_MAP } from "./player-icon-map";
import { Button } from "./ui/button.js";
import { Input } from "./ui/input.js";
import { OverflowMenu, type OverflowMenuItem } from "./ui/overflow-menu.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;
	onRollAllInitiative?: () => void;
	showRollAllInitiative?: boolean;
	rollAllInitiativeDisabled?: boolean;
	onOpenSourceManager?: () => void;
	autoFocus?: boolean;
}

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

function AddModeSuggestions({
	nameInput,
	suggestions,
	pcMatches,
	suggestionIndex,
	queued,
	onDismiss,
	onClickSuggestion,
	onSetSuggestionIndex,
	onSetQueued,
	onConfirmQueued,
	onAddFromPlayerCharacter,
	onClear,
}: {
	nameInput: string;
	suggestions: SearchResult[];
	pcMatches: PlayerCharacter[];
	suggestionIndex: number;
	queued: QueuedCreature | null;
	onDismiss: () => void;
	onClear: () => void;
	onClickSuggestion: (result: SearchResult) => void;
	onSetSuggestionIndex: (i: number) => void;
	onSetQueued: (q: QueuedCreature | null) => void;
	onConfirmQueued: () => void;
	onAddFromPlayerCharacter?: (pc: PlayerCharacter) => void;
}) {
	return (
		<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={onDismiss}
			>
				<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 = pc.icon
									? PLAYER_ICON_MAP[pc.icon as PlayerIcon]
									: undefined;
								const pcColor = pc.color
									? PLAYER_COLOR_HEX[pc.color as keyof typeof PLAYER_COLOR_HEX]
									: undefined;
								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);
												onClear();
											}}
										>
											{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={() => onClickSuggestion(result)}
										onMouseEnter={() => onSetSuggestionIndex(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) {
																onSetQueued(null);
															} else {
																onSetQueued({
																	...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();
															onSetQueued({
																...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();
															onConfirmQueued();
														}}
													>
														<Check className="h-3.5 w-3.5" />
													</button>
												</>
											) : (
												result.sourceDisplayName
											)}
										</span>
									</button>
								</li>
							);
						})}
					</ul>
				)}
			</div>
		</div>
	);
}

function buildOverflowItems(opts: {
	onManagePlayers?: () => void;
	onOpenSourceManager?: () => void;
	bestiaryLoaded: boolean;
	onBulkImport?: () => void;
	bulkImportDisabled?: boolean;
}): OverflowMenuItem[] {
	const items: OverflowMenuItem[] = [];
	if (opts.onManagePlayers) {
		items.push({
			icon: <Users className="h-4 w-4" />,
			label: "Player Characters",
			onClick: opts.onManagePlayers,
		});
	}
	if (opts.onOpenSourceManager) {
		items.push({
			icon: <Library className="h-4 w-4" />,
			label: "Manage Sources",
			onClick: opts.onOpenSourceManager,
		});
	}
	if (opts.bestiaryLoaded && opts.onBulkImport) {
		items.push({
			icon: <Import className="h-4 w-4" />,
			label: "Import All Sources",
			onClick: opts.onBulkImport,
			disabled: opts.bulkImportDisabled,
		});
	}
	return items;
}

export function ActionBar({
	onAddCombatant,
	onAddFromBestiary,
	bestiarySearch,
	bestiaryLoaded,
	onViewStatBlock,
	onBulkImport,
	bulkImportDisabled,
	inputRef,
	playerCharacters,
	onAddFromPlayerCharacter,
	onManagePlayers,
	onRollAllInitiative,
	showRollAllInitiative,
	rollAllInitiativeDisabled,
	onOpenSourceManager,
	autoFocus,
}: 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("");
	const [browseMode, setBrowseMode] = useState(false);

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

	const clearInput = () => {
		setNameInput("");
		setSuggestions([]);
		setPcMatches([]);
		setQueued(null);
		setSuggestionIndex(-1);
	};

	const dismissSuggestions = () => {
		setSuggestions([]);
		setPcMatches([]);
		setQueued(null);
		setSuggestionIndex(-1);
	};

	const confirmQueued = () => {
		if (!queued) return;
		for (let i = 0; i < queued.count; i++) {
			onAddFromBestiary(queued.result);
		}
		clearInput();
	};

	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 (browseMode) return;
		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 handleBrowseSearch = (value: string) => {
		setSuggestions(value.length >= 2 ? bestiarySearch(value) : []);
	};

	const handleAddSearch = (value: string) => {
		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 handleNameChange = (value: string) => {
		setNameInput(value);
		setSuggestionIndex(-1);
		if (browseMode) {
			handleBrowseSearch(value);
		} else {
			handleAddSearch(value);
		}
	};

	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") {
			dismissSuggestions();
		}
	};

	const handleBrowseKeyDown = (e: React.KeyboardEvent) => {
		if (e.key === "Escape") {
			setBrowseMode(false);
			clearInput();
			return;
		}
		if (suggestions.length === 0) 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" && suggestionIndex >= 0) {
			e.preventDefault();
			onViewStatBlock?.(suggestions[suggestionIndex]);
			setBrowseMode(false);
			clearInput();
		}
	};

	const handleBrowseSelect = (result: SearchResult) => {
		onViewStatBlock?.(result);
		setBrowseMode(false);
		clearInput();
	};

	const toggleBrowseMode = () => {
		setBrowseMode((m) => !m);
		clearInput();
		clearCustomFields();
	};

	const overflowItems = buildOverflowItems({
		onManagePlayers,
		onOpenSourceManager,
		bestiaryLoaded,
		onBulkImport,
		bulkImportDisabled,
	});

	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="flex-1">
					<div className="relative max-w-xs">
						<Input
							ref={inputRef}
							type="text"
							value={nameInput}
							onChange={(e) => handleNameChange(e.target.value)}
							onKeyDown={browseMode ? handleBrowseKeyDown : handleKeyDown}
							placeholder={
								browseMode ? "Search stat blocks..." : "+ Add combatants"
							}
							className="pr-8"
							autoFocus={autoFocus}
						/>
						{bestiaryLoaded && onViewStatBlock && (
							<button
								type="button"
								tabIndex={-1}
								className={cn(
									"absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-hover-neutral",
									browseMode && "text-accent",
								)}
								onClick={toggleBrowseMode}
								title={browseMode ? "Switch to add mode" : "Browse stat blocks"}
								aria-label={
									browseMode ? "Switch to add mode" : "Browse stat blocks"
								}
							>
								{browseMode ? (
									<EyeOff className="h-4 w-4" />
								) : (
									<Eye className="h-4 w-4" />
								)}
							</button>
						)}
						{browseMode && suggestions.length > 0 && (
							<div className="absolute bottom-full z-50 mb-1 w-full rounded-md border border-border bg-card shadow-lg">
								<ul className="max-h-48 overflow-y-auto py-1">
									{suggestions.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 === suggestionIndex
														? "bg-accent/20 text-foreground"
														: "text-foreground hover:bg-hover-neutral-bg"
												}`}
												onMouseDown={(e) => e.preventDefault()}
												onClick={() => handleBrowseSelect(result)}
												onMouseEnter={() => setSuggestionIndex(i)}
											>
												<span>{result.name}</span>
												<span className="text-xs text-muted-foreground">
													{result.sourceDisplayName}
												</span>
											</button>
										</li>
									))}
								</ul>
							</div>
						)}
						{!browseMode && hasSuggestions && (
							<AddModeSuggestions
								nameInput={nameInput}
								suggestions={suggestions}
								pcMatches={pcMatches}
								suggestionIndex={suggestionIndex}
								queued={queued}
								onDismiss={dismissSuggestions}
								onClear={clearInput}
								onClickSuggestion={handleClickSuggestion}
								onSetSuggestionIndex={setSuggestionIndex}
								onSetQueued={setQueued}
								onConfirmQueued={confirmQueued}
								onAddFromPlayerCharacter={onAddFromPlayerCharacter}
							/>
						)}
					</div>
				</div>
				{!browseMode && 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>
				)}
				{!browseMode && nameInput.length >= 2 && !hasSuggestions && (
					<Button type="submit">Add</Button>
				)}
				{showRollAllInitiative && onRollAllInitiative && (
					<Button
						type="button"
						size="icon"
						variant="ghost"
						className="text-muted-foreground hover:text-hover-action"
						onClick={onRollAllInitiative}
						disabled={rollAllInitiativeDisabled}
						title="Roll all initiative"
						aria-label="Roll all initiative"
					>
						<D20Icon className="h-6 w-6" />
					</Button>
				)}
				{overflowItems.length > 0 && <OverflowMenu items={overflowItems} />}
			</form>
		</div>
	);
}
