import {
	rollAllInitiativeUseCase,
	rollInitiativeUseCase,
} from "@initiative/application";
import type { CombatantId, Creature, CreatureId } from "@initiative/domain";
import { useCallback, useEffect, useRef, useState } from "react";
import { ActionBar } from "./components/action-bar";
import { CombatantRow } from "./components/combatant-row";
import { SourceManager } from "./components/source-manager";
import { StatBlockPanel } from "./components/stat-block-panel";
import { Toast } from "./components/toast";
import { TurnNavigation } from "./components/turn-navigation";
import { type SearchResult, useBestiary } from "./hooks/use-bestiary";
import { useBulkImport } from "./hooks/use-bulk-import";
import { useEncounter } from "./hooks/use-encounter";

function rollDice(): number {
	return Math.floor(Math.random() * 20) + 1;
}

export function App() {
	const {
		encounter,
		advanceTurn,
		retreatTurn,
		addCombatant,
		clearEncounter,
		removeCombatant,
		editCombatant,
		setInitiative,
		setHp,
		adjustHp,
		setAc,
		toggleCondition,
		toggleConcentration,
		addFromBestiary,
		makeStore,
	} = useEncounter();

	const {
		search,
		getCreature,
		isLoaded,
		isSourceCached,
		fetchAndCacheSource,
		uploadAndCacheSource,
		refreshCache,
	} = useBestiary();

	const bulkImport = useBulkImport();

	const [selectedCreatureId, setSelectedCreatureId] =
		useState<CreatureId | null>(null);
	const [bulkImportMode, setBulkImportMode] = useState(false);
	const [sourceManagerOpen, setSourceManagerOpen] = useState(false);

	const selectedCreature: Creature | null = selectedCreatureId
		? (getCreature(selectedCreatureId) ?? null)
		: null;

	const handleAddFromBestiary = useCallback(
		(result: SearchResult) => {
			addFromBestiary(result);
			// Derive the creature ID so stat block panel can try to show it
			const slug = result.name
				.toLowerCase()
				.replace(/[^a-z0-9]+/g, "-")
				.replace(/(^-|-$)/g, "");
			setSelectedCreatureId(
				`${result.source.toLowerCase()}:${slug}` as CreatureId,
			);
		},
		[addFromBestiary],
	);

	const handleCombatantStatBlock = useCallback((creatureId: string) => {
		setSelectedCreatureId(creatureId as CreatureId);
	}, []);

	const handleRollInitiative = useCallback(
		(id: CombatantId) => {
			rollInitiativeUseCase(makeStore(), id, rollDice(), getCreature);
		},
		[makeStore, getCreature],
	);

	const handleRollAllInitiative = useCallback(() => {
		rollAllInitiativeUseCase(makeStore(), rollDice, getCreature);
	}, [makeStore, getCreature]);

	const handleBulkImport = useCallback(() => {
		setBulkImportMode(true);
		setSelectedCreatureId(null);
	}, []);

	const handleStartBulkImport = useCallback(
		(baseUrl: string) => {
			bulkImport.startImport(
				baseUrl,
				fetchAndCacheSource,
				isSourceCached,
				refreshCache,
			);
		},
		[bulkImport.startImport, fetchAndCacheSource, isSourceCached, refreshCache],
	);

	const handleBulkImportDone = useCallback(() => {
		setBulkImportMode(false);
		bulkImport.reset();
	}, [bulkImport.reset]);

	// Auto-scroll to the active combatant when the turn changes
	const activeRowRef = useRef<HTMLDivElement>(null);
	useEffect(() => {
		activeRowRef.current?.scrollIntoView({
			block: "nearest",
			behavior: "smooth",
		});
	}, [encounter.activeIndex]);

	// Auto-show stat block for the active combatant when turn changes,
	// but only when the viewport is wide enough to show it alongside the tracker.
	// Only react to activeIndex changes — not combatant reordering (e.g. Roll All).
	const prevActiveIndexRef = useRef(encounter.activeIndex);
	useEffect(() => {
		if (prevActiveIndexRef.current === encounter.activeIndex) return;
		prevActiveIndexRef.current = encounter.activeIndex;
		if (!window.matchMedia("(min-width: 1024px)").matches) return;
		const active = encounter.combatants[encounter.activeIndex];
		if (!active?.creatureId || !isLoaded) return;
		setSelectedCreatureId(active.creatureId as CreatureId);
	}, [encounter.activeIndex, encounter.combatants, isLoaded]);

	return (
		<div className="flex h-screen flex-col">
			<div className="mx-auto flex w-full max-w-2xl flex-1 flex-col gap-3 px-4 min-h-0">
				{/* Turn Navigation — fixed at top */}
				<div className="shrink-0 pt-8">
					<TurnNavigation
						encounter={encounter}
						onAdvanceTurn={advanceTurn}
						onRetreatTurn={retreatTurn}
						onClearEncounter={clearEncounter}
						onRollAllInitiative={handleRollAllInitiative}
						onOpenSourceManager={() => setSourceManagerOpen((o) => !o)}
					/>
				</div>

				{sourceManagerOpen && (
					<div className="shrink-0 rounded-md border border-border bg-card px-4 py-3">
						<SourceManager onCacheCleared={refreshCache} />
					</div>
				)}

				{/* Scrollable area — combatant list */}
				<div className="flex-1 overflow-y-auto min-h-0">
					<div className="flex flex-col px-2 py-2">
						{encounter.combatants.length === 0 ? (
							<p className="py-12 text-center text-sm text-muted-foreground">
								No combatants yet — add one to get started
							</p>
						) : (
							encounter.combatants.map((c, i) => (
								<CombatantRow
									key={c.id}
									ref={i === encounter.activeIndex ? activeRowRef : null}
									combatant={c}
									isActive={i === encounter.activeIndex}
									onRename={editCombatant}
									onSetInitiative={setInitiative}
									onRemove={removeCombatant}
									onSetHp={setHp}
									onAdjustHp={adjustHp}
									onSetAc={setAc}
									onToggleCondition={toggleCondition}
									onToggleConcentration={toggleConcentration}
									onShowStatBlock={
										c.creatureId
											? () => handleCombatantStatBlock(c.creatureId as string)
											: undefined
									}
									onRollInitiative={
										c.creatureId ? handleRollInitiative : undefined
									}
								/>
							))
						)}
					</div>
				</div>

				{/* Action Bar — fixed at bottom */}
				<div className="shrink-0 pb-8">
					<ActionBar
						onAddCombatant={addCombatant}
						onAddFromBestiary={handleAddFromBestiary}
						bestiarySearch={search}
						bestiaryLoaded={isLoaded}
						onBulkImport={handleBulkImport}
						bulkImportDisabled={bulkImport.state.status === "loading"}
					/>
				</div>
			</div>

			{/* Stat Block Panel */}
			<StatBlockPanel
				creatureId={selectedCreatureId}
				creature={selectedCreature}
				isSourceCached={isSourceCached}
				fetchAndCacheSource={fetchAndCacheSource}
				uploadAndCacheSource={uploadAndCacheSource}
				refreshCache={refreshCache}
				onClose={() => {
					setSelectedCreatureId(null);
					setBulkImportMode(false);
				}}
				bulkImportMode={bulkImportMode}
				bulkImportState={bulkImport.state}
				onStartBulkImport={handleStartBulkImport}
				onBulkImportDone={handleBulkImportDone}
			/>

			{/* Toast for bulk import progress when panel is closed */}
			{bulkImport.state.status === "loading" && !bulkImportMode && (
				<Toast
					message={`Loading sources... ${bulkImport.state.completed + bulkImport.state.failed}/${bulkImport.state.total}`}
					progress={
						bulkImport.state.total > 0
							? (bulkImport.state.completed + bulkImport.state.failed) /
								bulkImport.state.total
							: 0
					}
					onDismiss={() => {}}
				/>
			)}
			{bulkImport.state.status === "complete" && !bulkImportMode && (
				<Toast
					message="All sources loaded"
					onDismiss={bulkImport.reset}
					autoDismissMs={3000}
				/>
			)}
			{bulkImport.state.status === "partial-failure" && !bulkImportMode && (
				<Toast
					message={`Loaded ${bulkImport.state.completed}/${bulkImport.state.total} sources (${bulkImport.state.failed} failed)`}
					onDismiss={bulkImport.reset}
				/>
			)}
		</div>
	);
}
