import { toast } from "@unom/ui/toast";
import { Plus, X } from "lucide-react";
import { useEffect, useState } from "react";
import {
	type Detected,
	type EmulatorDef,
	getEmulators,
	getPlatforms,
	type Platform,
	runDetect,
} from "../api.js";
import { Badge } from "../components/ui/badge.js";
import { Button } from "../components/ui/button.js";
import {
	Card,
	CardContent,
	CardDescription,
	CardHeader,
	CardTitle,
} from "../components/ui/card.js";
import { Input, Select } from "../components/ui/input.js";
import { useConfig } from "../hooks.js";

export const Emulators = () => {
	const { config, setConfig, save, saving } = useConfig();
	const [defs, setDefs] = useState<EmulatorDef[]>([]);
	const [detected, setDetected] = useState<Detected[]>([]);
	const [platforms, setPlatforms] = useState<Platform[]>([]);
	const [detecting, setDetecting] = useState(false);

	const load = () =>
		getEmulators().then((e) => {
			setDefs(e.defs);
			setDetected(e.detected);
		});
	useEffect(() => {
		load();
		getPlatforms()
			.then(setPlatforms)
			.catch(() => {});
	}, []);

	const detectedById = new Map(detected.map((d) => [d.id, d]));
	const onSave = async (next = config) => {
		if (next && (await save(next))) toast.success("Saved — syncing library");
	};

	return (
		<>
			<Card>
				<CardHeader className="flex-row items-start justify-between gap-4">
					<div className="space-y-1.5">
						<CardTitle>Detected emulators</CardTitle>
						<CardDescription>
							Best-effort detection: PATH, Flatpak, then known install paths.
						</CardDescription>
					</div>
					<Button
						size="sm"
						variant="outline"
						disabled={detecting}
						onClick={async () => {
							setDetecting(true);
							try {
								setDetected(await runDetect());
								toast.success("Re-detected emulators");
							} finally {
								setDetecting(false);
							}
						}}
					>
						{detecting ? "Detecting…" : "Re-detect"}
					</Button>
				</CardHeader>
				<CardContent>
					<div className="divide-y divide-border">
						{defs.map((def) => {
							const d = detectedById.get(def.id);
							return (
								<div
									key={def.id}
									className="flex items-center gap-3 py-2 text-sm"
								>
									<span className="font-medium">{def.name}</span>
									<span className="font-mono text-xs text-muted-foreground">
										{def.id}
									</span>
									{def.contested && <Badge variant="warn">contested</Badge>}
									<span className="ml-auto flex items-center gap-3">
										{d?.cores?.length ? (
											<span className="text-xs text-muted-foreground">
												{d.cores.length} cores
											</span>
										) : null}
										{d ? (
											<Badge variant="success">detected · {d.via}</Badge>
										) : (
											<Badge variant="outline">not found</Badge>
										)}
									</span>
								</div>
							);
						})}
					</div>
				</CardContent>
			</Card>

			{config && (
				<CustomEmulators
					config={config}
					setConfig={setConfig}
					onSave={onSave}
					saving={saving}
					reloadDefs={load}
				/>
			)}

			{config && (
				<Card>
					<CardHeader className="flex-row items-start justify-between gap-4">
						<div className="space-y-1.5">
							<CardTitle>Per-platform emulator</CardTitle>
							<CardDescription>
								The default emulator + core for each platform. Override to
								prefer a different emulator (including a custom one) or
								RetroArch core.
							</CardDescription>
						</div>
						<Button size="sm" disabled={saving} onClick={() => onSave()}>
							{saving ? "Saving…" : "Save & sync"}
						</Button>
					</CardHeader>
					<CardContent className="max-h-[420px] overflow-auto">
						<div className="divide-y divide-border">
							{platforms.map((p) => {
								const override = config.platformLaunch[p.id];
								const emulator = override?.emulator ?? p.defaultLaunch.emulator;
								const core = override?.core ?? p.defaultLaunch.core ?? "";
								const detCores = detectedById.get(emulator)?.cores ?? [];
								const setLaunch = (patch: {
									emulator?: string;
									core?: string;
								}) =>
									setConfig({
										...config,
										platformLaunch: {
											...config.platformLaunch,
											[p.id]: {
												emulator: patch.emulator ?? emulator,
												core: patch.core ?? core,
											},
										},
									});
								return (
									<div
										key={p.id}
										className="grid grid-cols-[1fr_180px_180px] items-center gap-3 py-2 text-sm"
									>
										<span>{p.name}</span>
										<Select
											value={emulator}
											onChange={(e) => setLaunch({ emulator: e.target.value })}
										>
											{defs.map((d) => (
												<option key={d.id} value={d.id}>
													{d.name}
													{detectedById.has(d.id) ? " ✓" : ""}
												</option>
											))}
										</Select>
										{emulator === "retroarch" && detCores.length ? (
											<Select
												value={core}
												onChange={(e) => setLaunch({ core: e.target.value })}
											>
												{!detCores.includes(core) && (
													<option value={core}>{core || "—"}</option>
												)}
												{detCores.map((c) => (
													<option key={c} value={c}>
														{c}
													</option>
												))}
											</Select>
										) : emulator === "retroarch" ? (
											<Input
												value={core}
												placeholder="snes9x"
												onChange={(e) => setLaunch({ core: e.target.value })}
											/>
										) : (
											<span className="text-muted-foreground">—</span>
										)}
									</div>
								);
							})}
						</div>
					</CardContent>
				</Card>
			)}
		</>
	);
};

// ── Custom emulator editor (design §6 escape hatch) ────────────────────────────────────────────
interface CustomProps {
	config: import("../api.js").Config;
	setConfig: (c: import("../api.js").Config) => void;
	onSave: (c?: import("../api.js").Config) => Promise<void>;
	saving: boolean;
	reloadDefs: () => Promise<void>;
}

const blankDraft = () => ({
	id: "",
	name: "",
	template: "{exe} {rom}",
	supportsArchives: false,
	detectLinux: "",
	detectWindows: "",
});

const CustomEmulators = ({
	config,
	setConfig,
	onSave,
	saving,
	reloadDefs,
}: CustomProps) => {
	const emulators = config.emulators ?? [];
	const [draft, setDraft] = useState(blankDraft());

	const add = async () => {
		const id = draft.id.trim();
		if (!/^[a-z][a-z0-9-]*$/.test(id)) {
			toast.error("Emulator id must be kebab-case (e.g. custom-dosbox)");
			return;
		}
		if (emulators.some((e) => e.id === id)) {
			toast.error(`An emulator with id "${id}" already exists`);
			return;
		}
		if (!draft.template.includes("{rom}")) {
			toast.error("Template must include {rom}");
			return;
		}
		const def: EmulatorDef = {
			id,
			name: draft.name.trim() || id,
			template: draft.template.trim(),
			supportsArchives: draft.supportsArchives,
			detect: {
				linux: draft.detectLinux
					.split(",")
					.map((s) => s.trim())
					.filter(Boolean),
				windows: draft.detectWindows
					.split(",")
					.map((s) => s.trim())
					.filter(Boolean),
			},
		};
		const next = { ...config, emulators: [...emulators, def] };
		setConfig(next);
		await onSave(next);
		await reloadDefs();
		setDraft(blankDraft());
	};

	const remove = async (id: string) => {
		const next = { ...config, emulators: emulators.filter((e) => e.id !== id) };
		setConfig(next);
		await onSave(next);
		await reloadDefs();
	};

	return (
		<Card>
			<CardHeader className="space-y-1.5">
				<CardTitle>Custom emulators</CardTitle>
				<CardDescription>
					Add an emulator Punktfunk doesn't ship a definition for. The template
					is trusted, run as the host user — use{" "}
					<code className="font-mono">{"{exe}"}</code>,{" "}
					<code className="font-mono">{"{rom}"}</code>, optionally{" "}
					<code className="font-mono">{"{core}"}</code>. Leave detection blank
					and bake the executable into the template if it isn't on PATH.
				</CardDescription>
			</CardHeader>
			<CardContent className="space-y-4">
				{emulators.length > 0 && (
					<div className="divide-y divide-border rounded-md border border-border">
						{emulators.map((e) => (
							<div key={e.id} className="flex items-center gap-3 p-2.5 text-sm">
								<span className="font-medium">{e.name}</span>
								<span className="font-mono text-xs text-muted-foreground">
									{e.id}
								</span>
								<span className="truncate font-mono text-xs text-muted-foreground">
									{e.template}
								</span>
								<Button
									variant="ghost"
									size="icon"
									className="ml-auto shrink-0"
									onClick={() => remove(e.id)}
									aria-label={`Remove ${e.id}`}
								>
									<X className="size-4" />
								</Button>
							</div>
						))}
					</div>
				)}

				<div className="grid grid-cols-2 gap-3 rounded-md border border-dashed border-border p-3">
					<label className="space-y-1 text-xs text-muted-foreground">
						id
						<Input
							value={draft.id}
							placeholder="custom-dosbox"
							onChange={(e) => setDraft({ ...draft, id: e.target.value })}
						/>
					</label>
					<label className="space-y-1 text-xs text-muted-foreground">
						Name
						<Input
							value={draft.name}
							placeholder="DOSBox"
							onChange={(e) => setDraft({ ...draft, name: e.target.value })}
						/>
					</label>
					<label className="col-span-2 space-y-1 text-xs text-muted-foreground">
						Template
						<Input
							className="font-mono"
							value={draft.template}
							placeholder="dosbox {rom} -fullscreen"
							onChange={(e) => setDraft({ ...draft, template: e.target.value })}
						/>
					</label>
					<label className="space-y-1 text-xs text-muted-foreground">
						Detect · Linux (comma)
						<Input
							value={draft.detectLinux}
							placeholder="dosbox, flatpak:io.dosbox"
							onChange={(e) =>
								setDraft({ ...draft, detectLinux: e.target.value })
							}
						/>
					</label>
					<label className="space-y-1 text-xs text-muted-foreground">
						Detect · Windows (comma)
						<Input
							value={draft.detectWindows}
							placeholder="C:\DOSBox\dosbox.exe"
							onChange={(e) =>
								setDraft({ ...draft, detectWindows: e.target.value })
							}
						/>
					</label>
					<label className="col-span-2 flex items-center gap-2 text-sm">
						<input
							type="checkbox"
							checked={draft.supportsArchives}
							onChange={(e) =>
								setDraft({ ...draft, supportsArchives: e.target.checked })
							}
						/>
						Can load .zip / .7z archives directly
					</label>
					<div className="col-span-2">
						<Button size="sm" disabled={saving} onClick={add}>
							<Plus className="size-4" /> Add emulator
						</Button>
					</div>
				</div>
			</CardContent>
		</Card>
	);
};
