import {
	rollAllInitiativeUseCase,
	rollInitiativeUseCase,
} from "@initiative/application";
import {
	type CombatantId,
	type Creature,
	type CreatureId,
	isDomainError,
} from "@initiative/domain";
import {
	useCallback,
	useEffect,
	useLayoutEffect,
	useRef,
	useState,
} from "react";
import { ActionBar } from "./components/action-bar";
import { BulkImportToasts } from "./components/bulk-import-toasts";
import { CombatantRow } from "./components/combatant-row";
import {
	PlayerCharacterSection,
	type PlayerCharacterSectionHandle,
} from "./components/player-character-section";
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";
import { usePlayerCharacters } from "./hooks/use-player-characters";
import { useSidePanelState } from "./hooks/use-side-panel-state";

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

function useActionBarAnimation(combatantCount: number) {
	const wasEmptyRef = useRef(combatantCount === 0);
	const [settling, setSettling] = useState(false);
	const [rising, setRising] = useState(false);
	const [topBarExiting, setTopBarExiting] = useState(false);

	useLayoutEffect(() => {
		const nowEmpty = combatantCount === 0;
		if (wasEmptyRef.current && !nowEmpty) {
			setSettling(true);
		} else if (!wasEmptyRef.current && nowEmpty) {
			setRising(true);
			setTopBarExiting(true);
		}
		wasEmptyRef.current = nowEmpty;
	}, [combatantCount]);

	const empty = combatantCount === 0;
	const risingClass = rising ? " animate-rise-to-center" : "";
	const settlingClass = settling ? " animate-settle-to-bottom" : "";
	const exitingClass = topBarExiting
		? " absolute inset-x-0 top-0 z-10 px-4 animate-slide-up-out"
		: "";
	const topBarClass = settling ? " animate-slide-down-in" : exitingClass;
	const showTopBar = !empty || topBarExiting;

	return {
		risingClass,
		settlingClass,
		topBarClass,
		showTopBar,
		onSettleEnd: () => setSettling(false),
		onRiseEnd: () => setRising(false),
		onTopBarExitEnd: () => setTopBarExiting(false),
	};
}

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

	const {
		characters: playerCharacters,
		createCharacter: createPlayerCharacter,
		editCharacter: editPlayerCharacter,
		deleteCharacter: deletePlayerCharacter,
	} = usePlayerCharacters();

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

	const bulkImport = useBulkImport();
	const sidePanel = useSidePanelState();

	const [rollSkippedCount, setRollSkippedCount] = useState(0);

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

	const pinnedCreature: Creature | null = sidePanel.pinnedCreatureId
		? (getCreature(sidePanel.pinnedCreatureId) ?? null)
		: null;

	const handleAddFromBestiary = useCallback(
		(result: SearchResult) => {
			addFromBestiary(result);
		},
		[addFromBestiary],
	);

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

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

	const handleRollAllInitiative = useCallback(() => {
		const result = rollAllInitiativeUseCase(makeStore(), rollDice, getCreature);
		if (!isDomainError(result) && result.skippedNoSource > 0) {
			setRollSkippedCount(result.skippedNoSource);
		}
	}, [makeStore, getCreature]);

	const handleViewStatBlock = useCallback(
		(result: SearchResult) => {
			const slug = result.name
				.toLowerCase()
				.replaceAll(/[^a-z0-9]+/g, "-")
				.replaceAll(/(^-|-$)/g, "");
			const cId = `${result.source.toLowerCase()}:${slug}` as CreatureId;
			sidePanel.showCreature(cId);
		},
		[sidePanel.showCreature],
	);

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

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

	const actionBarInputRef = useRef<HTMLInputElement>(null);
	const playerCharacterRef = useRef<PlayerCharacterSectionHandle>(null);
	const actionBarAnim = useActionBarAnimation(encounter.combatants.length);

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

	return (
		<div className="flex h-dvh flex-col">
			<div className="relative mx-auto flex min-h-0 w-full max-w-2xl flex-1 flex-col gap-3 px-4">
				{!!actionBarAnim.showTopBar && (
					<div
						className={`shrink-0 pt-8${actionBarAnim.topBarClass}`}
						onAnimationEnd={actionBarAnim.onTopBarExitEnd}
					>
						<TurnNavigation
							encounter={encounter}
							onAdvanceTurn={advanceTurn}
							onRetreatTurn={retreatTurn}
							onClearEncounter={clearEncounter}
						/>
					</div>
				)}

				{isEmpty ? (
					/* Empty state — ActionBar centered */
					<div className="flex min-h-0 flex-1 items-center justify-center pt-8 pb-[15%]">
						<div
							className={`w-full${actionBarAnim.risingClass}`}
							onAnimationEnd={actionBarAnim.onRiseEnd}
						>
							<ActionBar
								onAddCombatant={addCombatant}
								onAddFromBestiary={handleAddFromBestiary}
								bestiarySearch={search}
								bestiaryLoaded={isLoaded}
								onViewStatBlock={handleViewStatBlock}
								onBulkImport={sidePanel.showBulkImport}
								bulkImportDisabled={bulkImport.state.status === "loading"}
								inputRef={actionBarInputRef}
								playerCharacters={playerCharacters}
								onAddFromPlayerCharacter={addFromPlayerCharacter}
								onManagePlayers={() =>
									playerCharacterRef.current?.openManagement()
								}
								onRollAllInitiative={handleRollAllInitiative}
								showRollAllInitiative={hasCreatureCombatants}
								rollAllInitiativeDisabled={!canRollAllInitiative}
								onOpenSourceManager={sidePanel.showSourceManager}
								autoFocus
							/>
						</div>
					</div>
				) : (
					<>
						{/* Scrollable area — combatant list */}
						<div className="min-h-0 flex-1 overflow-y-auto">
							<div className="flex flex-col px-2 py-2">
								{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${actionBarAnim.settlingClass}`}
							onAnimationEnd={actionBarAnim.onSettleEnd}
						>
							<ActionBar
								onAddCombatant={addCombatant}
								onAddFromBestiary={handleAddFromBestiary}
								bestiarySearch={search}
								bestiaryLoaded={isLoaded}
								onViewStatBlock={handleViewStatBlock}
								onBulkImport={sidePanel.showBulkImport}
								bulkImportDisabled={bulkImport.state.status === "loading"}
								inputRef={actionBarInputRef}
								playerCharacters={playerCharacters}
								onAddFromPlayerCharacter={addFromPlayerCharacter}
								onManagePlayers={() =>
									playerCharacterRef.current?.openManagement()
								}
								onRollAllInitiative={handleRollAllInitiative}
								showRollAllInitiative={hasCreatureCombatants}
								rollAllInitiativeDisabled={!canRollAllInitiative}
								onOpenSourceManager={sidePanel.showSourceManager}
							/>
						</div>
					</>
				)}
			</div>

			{/* Pinned Stat Block Panel (left) */}
			{!!sidePanel.pinnedCreatureId && sidePanel.isWideDesktop && (
				<StatBlockPanel
					creatureId={sidePanel.pinnedCreatureId}
					creature={pinnedCreature}
					isSourceCached={isSourceCached}
					fetchAndCacheSource={fetchAndCacheSource}
					uploadAndCacheSource={uploadAndCacheSource}
					refreshCache={refreshCache}
					panelRole="pinned"
					isCollapsed={false}
					onToggleCollapse={() => {}}
					onPin={() => {}}
					onUnpin={sidePanel.unpin}
					showPinButton={false}
					side="left"
					onDismiss={() => {}}
				/>
			)}

			{/* Browse Stat Block Panel (right) */}
			<StatBlockPanel
				creatureId={sidePanel.selectedCreatureId}
				creature={selectedCreature}
				isSourceCached={isSourceCached}
				fetchAndCacheSource={fetchAndCacheSource}
				uploadAndCacheSource={uploadAndCacheSource}
				refreshCache={refreshCache}
				panelRole="browse"
				isCollapsed={sidePanel.isRightPanelCollapsed}
				onToggleCollapse={sidePanel.toggleCollapse}
				onPin={sidePanel.togglePin}
				onUnpin={() => {}}
				showPinButton={sidePanel.isWideDesktop && !!selectedCreature}
				side="right"
				onDismiss={sidePanel.dismissPanel}
				bulkImportMode={sidePanel.bulkImportMode}
				bulkImportState={bulkImport.state}
				onStartBulkImport={handleStartBulkImport}
				onBulkImportDone={handleBulkImportDone}
				sourceManagerMode={sidePanel.sourceManagerMode}
			/>

			<BulkImportToasts
				state={bulkImport.state}
				visible={!sidePanel.bulkImportMode || sidePanel.isRightPanelCollapsed}
				onReset={bulkImport.reset}
			/>

			{rollSkippedCount > 0 && (
				<Toast
					message={`${rollSkippedCount} skipped — bestiary source not loaded`}
					onDismiss={() => setRollSkippedCount(0)}
					autoDismissMs={4000}
				/>
			)}

			<PlayerCharacterSection
				ref={playerCharacterRef}
				characters={playerCharacters}
				onCreateCharacter={createPlayerCharacter}
				onEditCharacter={editPlayerCharacter}
				onDeleteCharacter={deletePlayerCharacter}
			/>
		</div>
	);
}
