import type { Creature, CreatureId } from "@initiative/domain";
import { PanelRightClose, Pin, PinOff } from "lucide-react";
import type { ReactNode } from "react";
import { useEffect, useState } from "react";
import { getSourceDisplayName } from "../adapters/bestiary-index-adapter.js";
import type { BulkImportState } from "../hooks/use-bulk-import.js";
import { useSwipeToDismiss } from "../hooks/use-swipe-to-dismiss.js";
import { BulkImportPrompt } from "./bulk-import-prompt.js";
import { SourceFetchPrompt } from "./source-fetch-prompt.js";
import { StatBlock } from "./stat-block.js";

interface StatBlockPanelProps {
	creatureId: CreatureId | null;
	creature: Creature | null;
	isSourceCached: (sourceCode: string) => Promise<boolean>;
	fetchAndCacheSource: (sourceCode: string, url: string) => Promise<void>;
	uploadAndCacheSource: (
		sourceCode: string,
		jsonData: unknown,
	) => Promise<void>;
	refreshCache: () => Promise<void>;
	panelRole: "browse" | "pinned";
	isFolded: boolean;
	onToggleFold: () => void;
	onPin: () => void;
	onUnpin: () => void;
	showPinButton: boolean;
	side: "left" | "right";
	onDismiss: () => void;
	bulkImportMode?: boolean;
	bulkImportState?: BulkImportState;
	onStartBulkImport?: (baseUrl: string) => void;
	onBulkImportDone?: () => void;
}

function extractSourceCode(cId: CreatureId): string {
	const colonIndex = cId.indexOf(":");
	if (colonIndex === -1) return "";
	return cId.slice(0, colonIndex).toUpperCase();
}

function FoldedTab({
	creatureName,
	side,
	onToggleFold,
}: {
	creatureName: string;
	side: "left" | "right";
	onToggleFold: () => void;
}) {
	return (
		<button
			type="button"
			onClick={onToggleFold}
			className={`flex h-full w-[40px] cursor-pointer items-center justify-center text-muted-foreground hover:text-hover-neutral ${
				side === "right" ? "self-start" : "self-end"
			}`}
			aria-label="Unfold stat block panel"
		>
			<span className="writing-vertical-rl text-sm font-medium">
				{creatureName}
			</span>
		</button>
	);
}

function PanelHeader({
	panelRole,
	showPinButton,
	onToggleFold,
	onPin,
	onUnpin,
}: {
	panelRole: "browse" | "pinned";
	showPinButton: boolean;
	onToggleFold: () => void;
	onPin: () => void;
	onUnpin: () => void;
}) {
	return (
		<div className="flex items-center justify-between border-b border-border px-4 py-2">
			<div className="flex items-center gap-1">
				{panelRole === "browse" && (
					<button
						type="button"
						onClick={onToggleFold}
						className="text-muted-foreground hover:text-hover-neutral"
						aria-label="Fold stat block panel"
					>
						<PanelRightClose className="h-4 w-4" />
					</button>
				)}
			</div>
			<div className="flex items-center gap-1">
				{panelRole === "browse" && showPinButton && (
					<button
						type="button"
						onClick={onPin}
						className="text-muted-foreground hover:text-hover-neutral"
						aria-label="Pin creature"
					>
						<Pin className="h-4 w-4" />
					</button>
				)}
				{panelRole === "pinned" && (
					<button
						type="button"
						onClick={onUnpin}
						className="text-muted-foreground hover:text-hover-neutral"
						aria-label="Unpin creature"
					>
						<PinOff className="h-4 w-4" />
					</button>
				)}
			</div>
		</div>
	);
}

function DesktopPanel({
	isFolded,
	side,
	creatureName,
	panelRole,
	showPinButton,
	onToggleFold,
	onPin,
	onUnpin,
	children,
}: {
	isFolded: boolean;
	side: "left" | "right";
	creatureName: string;
	panelRole: "browse" | "pinned";
	showPinButton: boolean;
	onToggleFold: () => void;
	onPin: () => void;
	onUnpin: () => void;
	children: ReactNode;
}) {
	const sideClasses = side === "left" ? "left-0 border-r" : "right-0 border-l";
	const foldedTranslate =
		side === "right"
			? "translate-x-[calc(100%-40px)]"
			: "translate-x-[calc(-100%+40px)]";

	return (
		<div
			className={`fixed top-0 bottom-0 flex w-[400px] flex-col border-border bg-card transition-slide-panel ${sideClasses} ${isFolded ? foldedTranslate : "translate-x-0"}`}
		>
			{isFolded ? (
				<FoldedTab
					creatureName={creatureName}
					side={side}
					onToggleFold={onToggleFold}
				/>
			) : (
				<>
					<PanelHeader
						panelRole={panelRole}
						showPinButton={showPinButton}
						onToggleFold={onToggleFold}
						onPin={onPin}
						onUnpin={onUnpin}
					/>
					<div className="flex-1 overflow-y-auto p-4">{children}</div>
				</>
			)}
		</div>
	);
}

function MobileDrawer({
	onDismiss,
	children,
}: {
	onDismiss: () => void;
	children: ReactNode;
}) {
	const { offsetX, isSwiping, handlers } = useSwipeToDismiss(onDismiss);

	return (
		<div className="fixed inset-0 z-50">
			<button
				type="button"
				className="absolute inset-0 bg-black/50 animate-in fade-in"
				onClick={onDismiss}
				aria-label="Close stat block"
			/>
			<div
				className={`absolute top-0 right-0 bottom-0 w-[85%] max-w-md border-l border-border bg-card shadow-xl ${isSwiping ? "" : "animate-slide-in-right"}`}
				style={
					isSwiping ? { transform: `translateX(${offsetX}px)` } : undefined
				}
				{...handlers}
			>
				<div className="flex items-center justify-between border-b border-border px-4 py-2">
					<button
						type="button"
						onClick={onDismiss}
						className="text-muted-foreground hover:text-hover-neutral"
						aria-label="Fold stat block panel"
					>
						<PanelRightClose className="h-4 w-4" />
					</button>
				</div>
				<div className="h-[calc(100%-41px)] overflow-y-auto p-4">
					{children}
				</div>
			</div>
		</div>
	);
}

export function StatBlockPanel({
	creatureId,
	creature,
	isSourceCached,
	fetchAndCacheSource,
	uploadAndCacheSource,
	refreshCache,
	panelRole,
	isFolded,
	onToggleFold,
	onPin,
	onUnpin,
	showPinButton,
	side,
	onDismiss,
	bulkImportMode,
	bulkImportState,
	onStartBulkImport,
	onBulkImportDone,
}: StatBlockPanelProps) {
	const [isDesktop, setIsDesktop] = useState(
		() => window.matchMedia("(min-width: 1024px)").matches,
	);
	const [needsFetch, setNeedsFetch] = useState(false);
	const [checkingCache, setCheckingCache] = useState(false);

	useEffect(() => {
		const mq = window.matchMedia("(min-width: 1024px)");
		const handler = (e: MediaQueryListEvent) => setIsDesktop(e.matches);
		mq.addEventListener("change", handler);
		return () => mq.removeEventListener("change", handler);
	}, []);

	useEffect(() => {
		if (!creatureId || creature) {
			setNeedsFetch(false);
			return;
		}

		const sourceCode = extractSourceCode(creatureId);
		if (!sourceCode) {
			setNeedsFetch(false);
			return;
		}

		setCheckingCache(true);
		isSourceCached(sourceCode).then((cached) => {
			setNeedsFetch(!cached);
			setCheckingCache(false);
		});
	}, [creatureId, creature, isSourceCached]);

	if (!creatureId && !bulkImportMode) return null;

	const sourceCode = creatureId ? extractSourceCode(creatureId) : "";

	const handleSourceLoaded = async () => {
		await refreshCache();
		setNeedsFetch(false);
	};

	const renderContent = () => {
		if (
			bulkImportMode &&
			bulkImportState &&
			onStartBulkImport &&
			onBulkImportDone
		) {
			return (
				<BulkImportPrompt
					importState={bulkImportState}
					onStartImport={onStartBulkImport}
					onDone={onBulkImportDone}
				/>
			);
		}

		if (checkingCache) {
			return (
				<div className="p-4 text-sm text-muted-foreground">Loading...</div>
			);
		}

		if (creature) {
			return <StatBlock creature={creature} />;
		}

		if (needsFetch && sourceCode) {
			return (
				<SourceFetchPrompt
					sourceCode={sourceCode}
					sourceDisplayName={getSourceDisplayName(sourceCode)}
					fetchAndCacheSource={fetchAndCacheSource}
					onSourceLoaded={handleSourceLoaded}
					onUploadSource={uploadAndCacheSource}
				/>
			);
		}

		return (
			<div className="p-4 text-sm text-muted-foreground">
				No stat block available
			</div>
		);
	};

	const creatureName =
		creature?.name ?? (bulkImportMode ? "Bulk Import" : "Creature");

	if (isDesktop) {
		return (
			<DesktopPanel
				isFolded={isFolded}
				side={side}
				creatureName={creatureName}
				panelRole={panelRole}
				showPinButton={showPinButton}
				onToggleFold={onToggleFold}
				onPin={onPin}
				onUnpin={onUnpin}
			>
				{renderContent()}
			</DesktopPanel>
		);
	}

	if (panelRole === "pinned") return null;

	return <MobileDrawer onDismiss={onDismiss}>{renderContent()}</MobileDrawer>;
}
