import type { PlayerCharacter } from "@initiative/domain";
import {
	Check,
	Download,
	Eye,
	EyeOff,
	Import,
	Library,
	Minus,
	Plus,
	Settings,
	Upload,
	Users,
} from "lucide-react";
import React, { type RefObject, useCallback, useState } from "react";
import type { SearchResult } from "../contexts/bestiary-context.js";
import { useBulkImportContext } from "../contexts/bulk-import-context.js";
import { useEncounterContext } from "../contexts/encounter-context.js";
import { useInitiativeRollsContext } from "../contexts/initiative-rolls-context.js";
import {
	creatureKey,
	type QueuedCreature,
	type SuggestionActions,
	useActionBarState,
} from "../hooks/use-action-bar-state.js";
import { useEncounterExportImport } from "../hooks/use-encounter-export-import.js";
import { useLongPress } from "../hooks/use-long-press.js";
import { cn } from "../lib/utils.js";
import { D20Icon } from "./d20-icon.js";
import { ExportMethodDialog } from "./export-method-dialog.js";
import { ImportConfirmDialog } from "./import-confirm-prompt.js";
import { ImportMethodDialog } from "./import-method-dialog.js";
import { PLAYER_COLOR_HEX, PLAYER_ICON_MAP } from "./player-icon-map.js";
import { RollModeMenu } from "./roll-mode-menu.js";
import { Toast } from "./toast.js";
import { Button } from "./ui/button.js";
import { Input } from "./ui/input.js";
import { OverflowMenu, type OverflowMenuItem } from "./ui/overflow-menu.js";

interface ActionBarProps {
	inputRef?: RefObject<HTMLInputElement | null>;
	autoFocus?: boolean;
	onManagePlayers?: () => void;
	onOpenSettings?: () => void;
}

interface AddModeSuggestionsProps {
	nameInput: string;
	suggestions: SearchResult[];
	pcMatches: PlayerCharacter[];
	suggestionIndex: number;
	queued: QueuedCreature | null;
	actions: SuggestionActions;
}

function AddModeSuggestions({
	nameInput,
	suggestions,
	pcMatches,
	suggestionIndex,
	queued,
	actions,
}: Readonly<AddModeSuggestionsProps>) {
	return (
		<div className="card-glow absolute bottom-full z-50 mb-1 w-full max-w-xs rounded-lg border border-border bg-card">
			<button
				type="button"
				className="flex w-full items-center gap-1.5 border-border border-b px-3 py-2 text-left text-accent text-sm hover:bg-accent/20"
				onMouseDown={(e) => e.preventDefault()}
				onClick={actions.dismiss}
			>
				<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-muted-foreground text-xs">
					Esc
				</kbd>
			</button>
			<div className="max-h-48 overflow-y-auto py-1">
				{pcMatches.length > 0 && (
					<>
						<div className="px-3 py-1 font-medium text-muted-foreground text-xs">
							Players
						</div>
						<ul>
							{pcMatches.map((pc) => {
								const PcIcon = pc.icon ? PLAYER_ICON_MAP[pc.icon] : undefined;
								const pcColor = pc.color
									? PLAYER_COLOR_HEX[pc.color]
									: 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-foreground text-sm hover:bg-hover-neutral-bg"
											onMouseDown={(e) => e.preventDefault()}
											onClick={() => {
												actions.addFromPlayerCharacter?.(pc);
												actions.clear();
											}}
										>
											{!!PcIcon && (
												<PcIcon size={14} style={{ color: pcColor }} />
											)}
											<span className="flex-1 truncate">{pc.name}</span>
											<span className="text-muted-foreground text-xs">
												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={cn(
											"flex w-full items-center justify-between px-3 py-1.5 text-left text-foreground text-sm",
											isQueued && "bg-accent/30",
											!isQueued && i === suggestionIndex && "bg-accent/20",
											!isQueued &&
												i !== suggestionIndex &&
												"hover:bg-hover-neutral-bg",
										)}
										onMouseDown={(e) => e.preventDefault()}
										onClick={() => actions.clickSuggestion(result)}
										onMouseEnter={() => actions.setSuggestionIndex(i)}
									>
										<span>{result.name}</span>
										<span className="flex items-center gap-1 text-muted-foreground text-xs">
											{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) {
																actions.setQueued(null);
															} else {
																actions.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-primary-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();
															actions.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();
															actions.confirmQueued();
														}}
													>
														<Check className="h-3.5 w-3.5" />
													</button>
												</>
											) : (
												result.sourceDisplayName
											)}
										</span>
									</button>
								</li>
							);
						})}
					</ul>
				)}
			</div>
		</div>
	);
}

interface BrowseSuggestionsProps {
	suggestions: SearchResult[];
	suggestionIndex: number;
	onSelect: (result: SearchResult) => void;
	onHover: (index: number) => void;
}

function BrowseSuggestions({
	suggestions,
	suggestionIndex,
	onSelect,
	onHover,
}: Readonly<BrowseSuggestionsProps>) {
	if (suggestions.length === 0) return null;

	return (
		<div className="card-glow absolute bottom-full z-50 mb-1 w-full rounded-lg border border-border bg-card">
			<ul className="max-h-48 overflow-y-auto py-1">
				{suggestions.map((result, i) => (
					<li key={creatureKey(result)}>
						<button
							type="button"
							className={cn(
								"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={() => onSelect(result)}
							onMouseEnter={() => onHover(i)}
						>
							<span>{result.name}</span>
							<span className="text-muted-foreground text-xs">
								{result.sourceDisplayName}
							</span>
						</button>
					</li>
				))}
			</ul>
		</div>
	);
}

interface CustomStatFieldsProps {
	customInit: string;
	customAc: string;
	customMaxHp: string;
	onInitChange: (v: string) => void;
	onAcChange: (v: string) => void;
	onMaxHpChange: (v: string) => void;
}

function CustomStatFields({
	customInit,
	customAc,
	customMaxHp,
	onInitChange,
	onAcChange,
	onMaxHpChange,
}: Readonly<CustomStatFieldsProps>) {
	return (
		<div className="hidden items-center gap-2 sm:flex">
			<Input
				type="text"
				inputMode="numeric"
				value={customInit}
				onChange={(e) => onInitChange(e.target.value)}
				placeholder="Init"
				className="w-16 text-center"
			/>
			<Input
				type="text"
				inputMode="numeric"
				value={customAc}
				onChange={(e) => onAcChange(e.target.value)}
				placeholder="AC"
				className="w-16 text-center"
			/>
			<Input
				type="text"
				inputMode="numeric"
				value={customMaxHp}
				onChange={(e) => onMaxHpChange(e.target.value)}
				placeholder="MaxHP"
				className="w-18 text-center"
			/>
		</div>
	);
}

function RollAllButton() {
	const { hasCreatureCombatants, canRollAllInitiative } = useEncounterContext();
	const { handleRollAllInitiative } = useInitiativeRollsContext();

	const [menuPos, setMenuPos] = useState<{
		x: number;
		y: number;
	} | null>(null);

	const openMenu = useCallback((x: number, y: number) => {
		setMenuPos({ x, y });
	}, []);

	const longPress = useLongPress(
		useCallback(
			(e: React.TouchEvent) => {
				const touch = e.touches[0];
				if (touch) openMenu(touch.clientX, touch.clientY);
			},
			[openMenu],
		),
	);

	if (!hasCreatureCombatants) return null;

	return (
		<>
			<Button
				type="button"
				size="icon"
				variant="ghost"
				className="text-muted-foreground hover:text-hover-action"
				onClick={() => handleRollAllInitiative()}
				onContextMenu={(e) => {
					e.preventDefault();
					openMenu(e.clientX, e.clientY);
				}}
				{...longPress}
				disabled={!canRollAllInitiative}
				title="Roll all initiative"
				aria-label="Roll all initiative"
			>
				<D20Icon className="h-6 w-6" />
			</Button>
			{!!menuPos && (
				<RollModeMenu
					position={menuPos}
					onSelect={(mode) => handleRollAllInitiative(mode)}
					onClose={() => setMenuPos(null)}
				/>
			)}
		</>
	);
}

function buildOverflowItems(opts: {
	onManagePlayers?: () => void;
	onOpenSourceManager?: () => void;
	bestiaryLoaded: boolean;
	onBulkImport?: () => void;
	bulkImportDisabled?: boolean;
	onExportEncounter: () => void;
	onImportEncounter: () => void;
	onOpenSettings?: () => void;
}): 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,
		});
	}
	items.push({
		icon: <Download className="h-4 w-4" />,
		label: "Export Encounter",
		onClick: opts.onExportEncounter,
	});
	items.push({
		icon: <Upload className="h-4 w-4" />,
		label: "Import Encounter",
		onClick: opts.onImportEncounter,
	});
	if (opts.onOpenSettings) {
		items.push({
			icon: <Settings className="h-4 w-4" />,
			label: "Settings",
			onClick: opts.onOpenSettings,
		});
	}
	return items;
}

export function ActionBar({
	inputRef,
	autoFocus,
	onManagePlayers,
	onOpenSettings,
}: Readonly<ActionBarProps>) {
	const {
		nameInput,
		suggestions,
		pcMatches,
		suggestionIndex,
		queued,
		customInit,
		customAc,
		customMaxHp,
		browseMode,
		bestiaryLoaded,
		hasSuggestions,
		showBulkImport,
		showSourceManager,
		suggestionActions,
		handleNameChange,
		handleKeyDown,
		handleBrowseKeyDown,
		handleAdd,
		handleBrowseSelect,
		toggleBrowseMode,
		setCustomInit,
		setCustomAc,
		setCustomMaxHp,
	} = useActionBarState();

	const { state: bulkImportState } = useBulkImportContext();

	const {
		importError,
		showExportMethod,
		showImportMethod,
		showImportConfirm,
		importFileRef,
		setImportError,
		setShowExportMethod,
		setShowImportMethod,
		handleExportDownload,
		handleExportClipboard,
		handleImportFile,
		handleImportClipboard,
		handleImportConfirm,
		handleImportCancel,
	} = useEncounterExportImport();

	const overflowItems = buildOverflowItems({
		onManagePlayers,
		onOpenSourceManager: showSourceManager,
		bestiaryLoaded,
		onBulkImport: showBulkImport,
		bulkImportDisabled: bulkImportState.status === "loading",
		onExportEncounter: () => setShowExportMethod(true),
		onImportEncounter: () => setShowImportMethod(true),
		onOpenSettings,
	});

	return (
		<div className="card-glow flex items-center gap-3 border-border border-t bg-card px-4 py-3 sm:rounded-lg sm:border">
			<form
				onSubmit={handleAdd}
				className="relative flex flex-1 flex-wrap items-center gap-3 sm:flex-nowrap"
			>
				<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 && (
							<button
								type="button"
								tabIndex={-1}
								className={cn(
									"absolute top-1/2 right-2 -translate-y-1/2 text-muted-foreground hover:text-hover-neutral",
									browseMode && "text-accent",
								)}
								onMouseDown={(e) => e.preventDefault()}
								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 && (
							<BrowseSuggestions
								suggestions={suggestions}
								suggestionIndex={suggestionIndex}
								onSelect={handleBrowseSelect}
								onHover={suggestionActions.setSuggestionIndex}
							/>
						)}
						{!browseMode && hasSuggestions && (
							<AddModeSuggestions
								nameInput={nameInput}
								suggestions={suggestions}
								pcMatches={pcMatches}
								suggestionIndex={suggestionIndex}
								queued={queued}
								actions={suggestionActions}
							/>
						)}
					</div>
				</div>
				{!browseMode && nameInput.length >= 2 && !hasSuggestions && (
					<CustomStatFields
						customInit={customInit}
						customAc={customAc}
						customMaxHp={customMaxHp}
						onInitChange={setCustomInit}
						onAcChange={setCustomAc}
						onMaxHpChange={setCustomMaxHp}
					/>
				)}
				{!browseMode && nameInput.length >= 2 && !hasSuggestions && (
					<Button type="submit">Add</Button>
				)}
				<RollAllButton />
				{overflowItems.length > 0 && <OverflowMenu items={overflowItems} />}
			</form>
			<input
				ref={importFileRef}
				type="file"
				accept=".json"
				className="hidden"
				onChange={handleImportFile}
			/>
			{!!importError && (
				<Toast
					message={importError}
					onDismiss={() => setImportError(null)}
					autoDismissMs={5000}
				/>
			)}
			<ExportMethodDialog
				open={showExportMethod}
				onDownload={handleExportDownload}
				onCopyToClipboard={handleExportClipboard}
				onClose={() => setShowExportMethod(false)}
			/>
			<ImportMethodDialog
				open={showImportMethod}
				onSelectFile={() => importFileRef.current?.click()}
				onSubmitClipboard={handleImportClipboard}
				onClose={() => setShowImportMethod(false)}
			/>
			<ImportConfirmDialog
				open={showImportConfirm}
				onConfirm={handleImportConfirm}
				onCancel={handleImportCancel}
			/>
		</div>
	);
}
