import { useMutation } from "@tanstack/react-query";
import { createFileRoute, redirect } from "@tanstack/react-router";
import { useCallback, useEffect, useRef, useState } from "react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { authClient } from "@/lib/auth-client";
import { DEDUCTION_PRESETS_CENTS, formatCents } from "@/lib/drinkkaart";
import { orpc } from "@/utils/orpc";

export const Route = createFileRoute("/drinkkaart")({
	beforeLoad: async () => {
		const session = await authClient.getSession();
		if (!session.data?.user) {
			throw redirect({ to: "/login" });
		}
		const user = session.data.user as { role?: string };
		if (user.role !== "admin") {
			throw redirect({ to: "/" });
		}
	},
	component: DrinkkaartScanPage,
});

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

type ScanState =
	| { step: "scanning" }
	| { step: "resolving" }
	| {
			step: "resolved";
			drinkkaartId: string;
			userId: string;
			userName: string;
			userEmail: string;
			balance: number;
	  }
	| {
			step: "confirming";
			drinkkaartId: string;
			userName: string;
			userEmail: string;
			balance: number;
			amountCents: number;
	  }
	| {
			step: "success";
			userName: string;
			amountCents: number;
			balanceAfter: number;
	  }
	| { step: "error"; message: string };

// ---------------------------------------------------------------------------
// ScanningView — full-screen camera with manual fallback
// ---------------------------------------------------------------------------

function ScanningView({ onScan }: { onScan: (token: string) => void }) {
	const videoContainerId = "qr-video-container";
	const scannerRef = useRef<unknown>(null);
	const [hasError, setHasError] = useState(false);
	const [manualToken, setManualToken] = useState("");
	const [showManual, setShowManual] = useState(false);
	const onScanRef = useRef(onScan);
	useEffect(() => {
		onScanRef.current = onScan;
	}, [onScan]);

	useEffect(() => {
		if (hasError) return;
		let stopped = false;

		import("html5-qrcode")
			.then(({ Html5Qrcode }) => {
				if (stopped) return;
				const scanner = new Html5Qrcode(videoContainerId);
				scannerRef.current = scanner;
				scanner
					.start(
						{ facingMode: "environment" },
						{ fps: 15, qrbox: { width: 240, height: 240 } },
						(decodedText: string) => {
							scanner
								.stop()
								.catch(console.error)
								.finally(() => {
									if (!stopped) onScanRef.current(decodedText);
								});
						},
						() => {},
					)
					.catch((err: unknown) => {
						if (!stopped) {
							console.error("Camera start failed:", err);
							setHasError(true);
							setShowManual(true);
						}
					});
			})
			.catch((err) => {
				console.error("Failed to load html5-qrcode:", err);
				setHasError(true);
				setShowManual(true);
			});

		return () => {
			stopped = true;
			const s = scannerRef.current as {
				stop: () => Promise<void>;
				clear: () => Promise<void>;
			} | null;
			s?.stop()
				.catch(() => {})
				.finally(() => s?.clear().catch(() => {}));
		};
	}, [hasError]);

	const handleManualSubmit = () => {
		const token = manualToken.trim();
		if (token) onScan(token);
	};

	return (
		<div className="flex flex-1 flex-col">
			{/* Camera — fills all available space above the hint */}
			{!hasError && (
				<div
					id={videoContainerId}
					className="[&_#qr-shaded-region]:!border-white/40 flex-1 overflow-hidden bg-black [&>video]:h-full [&>video]:w-full [&>video]:object-cover"
				/>
			)}

			{hasError && (
				<div className="flex flex-1 flex-col items-center justify-center px-6">
					<div className="w-full max-w-sm rounded-2xl border border-white/10 bg-white/5 p-6 text-center">
						<p className="text-white/60">Camera niet beschikbaar.</p>
					</div>
				</div>
			)}

			{/* Bottom controls */}
			<div className="shrink-0 space-y-3 px-4 pt-4 pb-8">
				{showManual ? (
					<div className="flex gap-2">
						<Input
							autoFocus
							value={manualToken}
							onChange={(e) => setManualToken(e.target.value)}
							placeholder="Plak QR token..."
							className="border-white/20 bg-white/10 text-white placeholder:text-white/30"
							onKeyDown={(e) => e.key === "Enter" && handleManualSubmit()}
						/>
						<Button
							onClick={handleManualSubmit}
							disabled={!manualToken.trim()}
							className="shrink-0 bg-white text-[#214e51] hover:bg-white/90"
						>
							OK
						</Button>
					</div>
				) : (
					<div className="flex items-center justify-between">
						<p className="text-sm text-white/40">
							Richt de camera op de QR-code
						</p>
						<button
							type="button"
							onClick={() => setShowManual(true)}
							className="text-sm text-white/30 hover:text-white/60"
						>
							Handmatig
						</button>
					</div>
				)}
			</div>
		</div>
	);
}

// ---------------------------------------------------------------------------
// Page
// ---------------------------------------------------------------------------

function DrinkkaartScanPage() {
	const [scanState, setScanState] = useState<ScanState>({ step: "scanning" });
	const [customCents, setCustomCents] = useState("");
	const [useCustom, setUseCustom] = useState(false);
	const [selectedCents, setSelectedCents] = useState<number>(
		DEDUCTION_PRESETS_CENTS[1],
	);
	const successTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

	// Auto-return to scanner 1.5s after success
	useEffect(() => {
		if (scanState.step === "success") {
			successTimerRef.current = setTimeout(() => {
				setScanState({ step: "scanning" });
				setCustomCents("");
				setUseCustom(false);
				setSelectedCents(DEDUCTION_PRESETS_CENTS[1]);
			}, 1500);
		}
		return () => {
			if (successTimerRef.current) clearTimeout(successTimerRef.current);
		};
	}, [scanState.step]);

	// --- Mutations ---
	const resolveQrMutation = useMutation(
		orpc.drinkkaart.resolveQrToken.mutationOptions(),
	);
	const deductMutation = useMutation(
		orpc.drinkkaart.deductBalance.mutationOptions(),
	);

	// --- Handlers ---
	const handleScan = useCallback(
		(token: string) => {
			setScanState({ step: "resolving" });
			resolveQrMutation.mutate(
				{ token },
				{
					onSuccess: (data) => {
						setScanState({
							step: "resolved",
							drinkkaartId: data.drinkkaartId,
							userId: data.userId,
							userName: data.userName,
							userEmail: data.userEmail,
							balance: data.balance,
						});
					},
					onError: (err: Error) => {
						setScanState({
							step: "error",
							message: err.message ?? "Ongeldig QR-token",
						});
					},
				},
			);
		},
		[resolveQrMutation],
	);

	const handlePresetSelect = (cents: number) => {
		setSelectedCents(cents);
		setUseCustom(false);
	};

	const getAmountCents = () => {
		if (useCustom) {
			return Math.round((Number.parseFloat(customCents || "0") || 0) * 100);
		}
		return selectedCents;
	};

	const handleDeductTap = () => {
		if (scanState.step !== "resolved") return;
		const amountCents = getAmountCents();
		if (!amountCents || amountCents < 1 || amountCents > scanState.balance)
			return;
		setScanState({
			step: "confirming",
			drinkkaartId: scanState.drinkkaartId,
			userName: scanState.userName,
			userEmail: scanState.userEmail,
			balance: scanState.balance,
			amountCents,
		});
	};

	const handleConfirm = () => {
		if (scanState.step !== "confirming") return;
		deductMutation.mutate(
			{
				drinkkaartId: scanState.drinkkaartId,
				amountCents: scanState.amountCents,
			},
			{
				onSuccess: (data) => {
					toast.success(`${formatCents(scanState.amountCents)} afgeschreven`);
					setScanState({
						step: "success",
						userName: scanState.userName,
						amountCents: scanState.amountCents,
						balanceAfter: data.balanceAfter,
					});
					deductMutation.reset();
				},
				onError: (err: Error) => {
					setScanState({
						step: "error",
						message: err.message ?? "Fout bij afschrijving",
					});
					deductMutation.reset();
				},
			},
		);
	};

	const restartScanner = () => {
		setScanState({ step: "scanning" });
		setCustomCents("");
		setUseCustom(false);
		setSelectedCents(DEDUCTION_PRESETS_CENTS[1]);
		deductMutation.reset();
	};

	// Determine the display amount for the deduct button (only in resolved step)
	const resolvedAmountCents =
		scanState.step === "resolved" ? getAmountCents() : 0;
	const resolvedBalance = scanState.step === "resolved" ? scanState.balance : 0;
	const deductIsValid =
		scanState.step === "resolved" &&
		Number.isInteger(resolvedAmountCents) &&
		resolvedAmountCents >= 1 &&
		resolvedAmountCents <= resolvedBalance;

	// ---------------------------------------------------------------------------
	// Render
	// ---------------------------------------------------------------------------

	return (
		<div className="flex h-dvh flex-col bg-[#214e51]">
			{/* ------------------------------------------------------------------ */}
			{/* Header                                                               */}
			{/* ------------------------------------------------------------------ */}
			<header className="flex shrink-0 items-center justify-between border-white/10 border-b px-4 py-3">
				<h1 className="font-['Intro',sans-serif] text-lg text-white">
					Drinkkaart
				</h1>
				{/* Only show the admin link when not mid-flow */}
				{(scanState.step === "scanning" || scanState.step === "error") && (
					<a
						href="/admin/drinkkaart"
						className="rounded-lg px-3 py-1.5 text-sm text-white/50 hover:bg-white/10 hover:text-white"
					>
						Beheer →
					</a>
				)}
				{scanState.step === "resolved" && (
					<button
						type="button"
						onClick={restartScanner}
						className="rounded-lg px-3 py-1.5 text-sm text-white/50 hover:bg-white/10 hover:text-white"
					>
						Annuleer
					</button>
				)}
				{scanState.step === "confirming" && (
					<button
						type="button"
						onClick={() => {
							// Go back to resolved state
							const s = scanState;
							setScanState({
								step: "resolved",
								drinkkaartId: s.drinkkaartId,
								userId: "",
								userName: s.userName,
								userEmail: s.userEmail,
								balance: s.balance,
							});
						}}
						className="rounded-lg px-3 py-1.5 text-sm text-white/50 hover:bg-white/10 hover:text-white"
					>
						← Terug
					</button>
				)}
			</header>

			{/* ------------------------------------------------------------------ */}
			{/* Main content area                                                    */}
			{/* ------------------------------------------------------------------ */}
			<main className="flex flex-1 flex-col overflow-hidden">
				{/* ---- SCANNING ---- */}
				{scanState.step === "scanning" && <ScanningView onScan={handleScan} />}

				{/* ---- RESOLVING ---- */}
				{scanState.step === "resolving" && (
					<div className="flex flex-1 flex-col items-center justify-center gap-4 px-6">
						<div className="h-12 w-12 animate-spin rounded-full border-4 border-white/20 border-t-white" />
						<p className="text-white/60">Controleren...</p>
					</div>
				)}

				{/* ---- RESOLVED: show person + deduct controls ---- */}
				{scanState.step === "resolved" && (
					<div className="flex flex-1 flex-col overflow-y-auto">
						{/* Person card */}
						<div className="mx-4 mt-4 rounded-2xl border border-white/10 bg-white/5 p-5">
							<div className="flex items-start justify-between">
								<div className="min-w-0 flex-1">
									<p className="truncate font-semibold text-lg text-white">
										{scanState.userName}
									</p>
									<p className="truncate text-sm text-white/50">
										{scanState.userEmail}
									</p>
								</div>
								<div className="ml-4 shrink-0 text-right">
									<p className="text-white/40 text-xs">Saldo</p>
									<p className="font-['Intro',sans-serif] text-3xl text-white">
										{formatCents(scanState.balance)}
									</p>
								</div>
							</div>
						</div>

						{/* Amount selection */}
						<div className="mx-4 mt-4 space-y-4">
							<p className="text-sm text-white/60">Bedrag kiezen</p>

							{/* Preset grid */}
							<div className="grid grid-cols-4 gap-2">
								{DEDUCTION_PRESETS_CENTS.map((cents) => (
									<button
										key={cents}
										type="button"
										onClick={() => handlePresetSelect(cents)}
										className={`rounded-xl border py-4 font-semibold text-sm transition-colors ${
											!useCustom && selectedCents === cents
												? "border-white bg-white text-[#214e51]"
												: "border-white/20 text-white hover:border-white/50 active:bg-white/10"
										}`}
									>
										{formatCents(cents)}
									</button>
								))}
							</div>

							{/* Custom amount */}
							<div className="relative">
								<span className="absolute top-1/2 left-3.5 -translate-y-1/2 text-white/50">
									€
								</span>
								<Input
									type="number"
									min="0.01"
									step="0.01"
									placeholder="Eigen bedrag"
									value={customCents}
									onChange={(e) => {
										setCustomCents(e.target.value);
										setUseCustom(true);
									}}
									onFocus={() => setUseCustom(true)}
									className="border-white/20 bg-white/10 py-4 pl-8 text-base text-white placeholder:text-white/30"
								/>
							</div>

							{/* Insufficient balance warning */}
							{resolvedAmountCents > resolvedBalance && (
								<p className="text-red-400 text-sm">
									Onvoldoende saldo ({formatCents(resolvedBalance)})
								</p>
							)}
						</div>

						{/* Deduct button — sticky at bottom */}
						<div className="mt-auto px-4 pt-4 pb-8">
							<Button
								onClick={handleDeductTap}
								disabled={!deductIsValid}
								className="h-14 w-full bg-white font-semibold text-[#214e51] text-lg hover:bg-white/90 disabled:opacity-40"
							>
								{deductIsValid
									? `Afschrijven — ${formatCents(resolvedAmountCents)}`
									: "Kies een bedrag"}
							</Button>
						</div>
					</div>
				)}

				{/* ---- CONFIRMING ---- */}
				{scanState.step === "confirming" && (
					<div className="flex flex-1 flex-col items-center justify-center px-6">
						<div className="w-full max-w-sm space-y-6">
							{/* Summary card */}
							<div className="rounded-2xl border border-white/10 bg-white/5 p-6 text-center">
								<p className="text-sm text-white/60">Afschrijving voor</p>
								<p className="mt-1 font-semibold text-white text-xl">
									{scanState.userName}
								</p>
								<p className="mt-4 font-['Intro',sans-serif] text-5xl text-white">
									{formatCents(scanState.amountCents)}
								</p>
								<p className="mt-3 text-sm text-white/50">
									Nieuw saldo:{" "}
									<span className="text-white">
										{formatCents(scanState.balance - scanState.amountCents)}
									</span>
								</p>
							</div>

							{deductMutation.isError && (
								<p className="text-center text-red-400 text-sm">
									{(deductMutation.error as Error)?.message ??
										"Fout bij afschrijving"}
								</p>
							)}

							{/* Confirm button */}
							<Button
								onClick={handleConfirm}
								disabled={deductMutation.isPending}
								className="h-14 w-full bg-white font-semibold text-[#214e51] text-lg hover:bg-white/90 disabled:opacity-50"
							>
								{deductMutation.isPending ? "Verwerken..." : "Bevestigen"}
							</Button>
						</div>
					</div>
				)}

				{/* ---- SUCCESS ---- */}
				{scanState.step === "success" && (
					<div className="flex flex-1 flex-col items-center justify-center px-6">
						<div className="w-full max-w-sm space-y-4 text-center">
							<div className="mx-auto flex h-16 w-16 items-center justify-center rounded-full bg-green-400/20">
								<svg
									viewBox="0 0 24 24"
									fill="none"
									stroke="currentColor"
									strokeWidth={2.5}
									strokeLinecap="round"
									strokeLinejoin="round"
									className="h-8 w-8 text-green-400"
									aria-label="Betaling geslaagd"
								>
									<polyline points="20 6 9 17 4 12" />
								</svg>
							</div>
							<div>
								<p className="font-semibold text-green-300 text-xl">
									{formatCents(scanState.amountCents)} afgeschreven
								</p>
								<p className="mt-1 text-sm text-white/60">
									{scanState.userName} — nieuw saldo:{" "}
									<strong className="text-white">
										{formatCents(scanState.balanceAfter)}
									</strong>
								</p>
							</div>
							<p className="text-white/30 text-xs">
								Volgende scan wordt gestart…
							</p>
						</div>
					</div>
				)}

				{/* ---- ERROR ---- */}
				{scanState.step === "error" && (
					<div className="flex flex-1 flex-col items-center justify-center px-6">
						<div className="w-full max-w-sm space-y-5">
							<div className="rounded-2xl border border-red-400/20 bg-red-400/5 p-5 text-center">
								<p className="font-semibold text-lg text-red-300">Fout</p>
								<p className="mt-2 text-sm text-white/60">
									{scanState.message}
								</p>
							</div>
							<Button
								onClick={restartScanner}
								className="h-14 w-full bg-white font-semibold text-[#214e51] text-lg hover:bg-white/90"
							>
								Opnieuw scannen
							</Button>
						</div>
					</div>
				)}
			</main>
		</div>
	);
}
