import React, { useCallback, useEffect, useRef, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { TerminalComponent, type TerminalRef } from "../components/terminal";
import {
	TerminalSessionTabs,
	type TerminalSessionInfo,
} from "../components/terminal-session-tabs";
import { Icon } from "../components/icon";
import { SpecialKeysStrip } from "../components/special-keys-strip";
import { SpecialKeysPanel } from "../components/special-keys-panel";
import { useMobileViewport } from "../hooks/use-mobile-viewport";
import { useAutoHide } from "../hooks/use-auto-hide";
import { useVirtualKeyboard } from "../hooks/use-virtual-keyboard";
import { useTerminalSessions } from "../hooks/use-terminal-sessions";
import type { TerminalSession } from "../api/terminal";
import type { ModifierKey } from "../hooks/use-special-keys";

const SESSIONS_TO_INFO = (sessions: TerminalSession[]): TerminalSessionInfo[] =>
	sessions.map((s) => ({
		id: s.id,
		name: s.name,
		status: s.status as TerminalSessionInfo["status"],
	}));

type TerminalStatus =
	| "connecting"
	| "connected"
	| "disconnected"
	| "error"
	| "resetting";

export const TerminalPage: React.FC = () => {
	const { instanceId } = useParams<{
		instanceId: string;
	}>();
	const navigate = useNavigate();
	const isMobile = useMobileViewport();
	const [isFullscreen, setIsFullscreen] = useState(false);
	const terminalRefs = useRef<Record<string, React.RefObject<TerminalRef>>>({});
	const headerAutoHide = useAutoHide({ timeout: 3000, enabled: isMobile });

	// Track terminal status and callbacks for unified fullscreen header
	const [terminalStatuses, setTerminalStatuses] = useState<
		Record<string, TerminalStatus>
	>({});
	const changeFontSizeRef = useRef<((delta: number) => void) | null>(null);
	const sendDataRef = useRef<((data: string) => void) | null>(null);
	const focusInputRef = useRef<(() => void) | null>(null);
	const [showResetConfirm, setShowResetConfirm] = useState(false);
	const [showSpecialKeysPanel, setShowSpecialKeysPanel] = useState(false);
	const [activeModifier, setActiveModifier] = useState<ModifierKey | null>(
		null,
	);
	const { isOpen: isKeyboardOpen, height: keyboardHeight } =
		useVirtualKeyboard();

	const {
		sessions,
		activeSessionId,
		setActiveSessionId,
		createSession,
		closeSession,
		renameSession,
		resetSession,
		loading,
		error,
	} = useTerminalSessions(instanceId ?? "");

	// Auto-create default session if none exist after loading completes
	useEffect(() => {
		if (!loading && sessions.length === 0 && !error && instanceId) {
			void createSession("Session 1");
		}
	}, [loading, sessions.length, error, instanceId, createSession]);

	// Ensure refs map is kept in sync with sessions
	useEffect(() => {
		for (const session of sessions) {
			if (!terminalRefs.current[session.id]) {
				terminalRefs.current[session.id] = React.createRef<TerminalRef>();
			}
		}
		// Clean up refs for closed sessions
		const currentIds = new Set(sessions.map((s) => s.id));
		for (const id of Object.keys(terminalRefs.current)) {
			if (!currentIds.has(id)) {
				delete terminalRefs.current[id];
			}
		}
	}, [sessions]);

	// Fit and focus active terminal when switching tabs
	useEffect(() => {
		if (activeSessionId && terminalRefs.current[activeSessionId]) {
			const ref = terminalRefs.current[activeSessionId];
			// Double rAF ensures layout has settled after the display:block switch
			let raf1 = 0;
			let raf2 = 0;
			raf1 = requestAnimationFrame(() => {
				raf2 = requestAnimationFrame(() => {
					ref.current?.fit();
					ref.current?.focus();
				});
			});
			return () => {
				cancelAnimationFrame(raf1);
				cancelAnimationFrame(raf2);
			};
		}
	}, [activeSessionId]);

	// Keyboard shortcuts
	useEffect(() => {
		const handleKeyDown = (e: KeyboardEvent) => {
			const isAltShift = e.altKey && e.shiftKey && !e.ctrlKey && !e.metaKey;
			if (!isAltShift) return;

			switch (e.key.toLowerCase()) {
				case "n":
					e.preventDefault();
					if (sessions.length < 5) {
						void createSession(`Session ${sessions.length + 1}`);
					}
					break;
				case "w":
					e.preventDefault();
					if (
						activeSessionId &&
						window.confirm("Close this terminal session?")
					) {
						void closeSession(activeSessionId);
					}
					break;
				case "arrowleft":
					e.preventDefault();
					if (activeSessionId) {
						const idx = sessions.findIndex((s) => s.id === activeSessionId);
						if (idx > 0) {
							setActiveSessionId(sessions[idx - 1].id);
						}
					}
					break;
				case "arrowright":
					e.preventDefault();
					if (activeSessionId) {
						const idx = sessions.findIndex((s) => s.id === activeSessionId);
						if (idx < sessions.length - 1) {
							setActiveSessionId(sessions[idx + 1].id);
						}
					}
					break;
				case "r":
					e.preventDefault();
					if (activeSessionId) {
						void resetSession(activeSessionId);
					}
					break;
				case "f":
					e.preventDefault();
					setIsFullscreen((prev) => !prev);
					break;
				default:
					break;
			}
		};

		window.addEventListener("keydown", handleKeyDown);
		return () => window.removeEventListener("keydown", handleKeyDown);
	}, [
		sessions,
		activeSessionId,
		createSession,
		closeSession,
		resetSession,
		setActiveSessionId,
	]);

	// Keep screen awake while terminal is open
	useEffect(() => {
		let wakeLock: WakeLockSentinel | null = null;

		const requestWakeLock = async () => {
			try {
				if ("wakeLock" in navigator) {
					wakeLock = await navigator.wakeLock.request("screen");
				}
			} catch {
				// Wake lock may be denied; silently ignore
			}
		};

		void requestWakeLock();

		const handleVisibilityChange = () => {
			if (document.visibilityState === "visible") {
				void requestWakeLock();
			}
		};

		document.addEventListener("visibilitychange", handleVisibilityChange);
		return () => {
			document.removeEventListener("visibilitychange", handleVisibilityChange);
			wakeLock?.release().catch(() => {});
		};
	}, []);

	// Lock page scroll on mobile terminal so swipes scroll the terminal buffer,
	// not the page.
	useEffect(() => {
		if (!isMobile) return;
		document.documentElement.classList.add("terminal-page-open");
		document.body.classList.add("terminal-page-open");
		return () => {
			document.documentElement.classList.remove("terminal-page-open");
			document.body.classList.remove("terminal-page-open");
		};
	}, [isMobile]);

	// Click outside terminal content/header to exit fullscreen
	const handleFullscreenClick = useCallback(
		(e: React.MouseEvent<HTMLElement>) => {
			if (!isFullscreen) return;
			const target = e.target as Node;
			const current = e.currentTarget as HTMLElement;
			const content = current.querySelector(".terminal-page-content");
			const header = current.querySelector(".terminal-fullscreen-header");
			if (content?.contains(target) || header?.contains(target)) {
				return;
			}
			setIsFullscreen(false);
		},
		[isFullscreen],
	);

	const handleSelect = useCallback(
		(sessionId: string) => {
			setActiveSessionId(sessionId);
		},
		[setActiveSessionId],
	);

	const handleClose = useCallback(
		async (sessionId: string) => {
			await closeSession(sessionId);
		},
		[closeSession],
	);

	const handleCreate = useCallback(() => {
		void createSession(`Session ${sessions.length + 1}`);
	}, [createSession, sessions.length]);

	const handleRename = useCallback(
		(sessionId: string, newName: string) => {
			void renameSession(sessionId, newName);
		},
		[renameSession],
	);

	const handleTerminalReady = useCallback(
		(
			sendData: (data: string) => void,
			status: TerminalStatus,
			focusInput: () => void,
			changeFontSize: (delta: number) => void,
		) => {
			setTerminalStatuses((prev) => ({
				...prev,
				[activeSessionId ?? "default"]: status,
			}));
			sendDataRef.current = sendData;
			focusInputRef.current = focusInput;
			changeFontSizeRef.current = changeFontSize;
		},
		[activeSessionId],
	);

	const handleFontSizeChange = useCallback((delta: number) => {
		changeFontSizeRef.current?.(delta);
	}, []);

	const handleSendKey = useCallback((data: string) => {
		sendDataRef.current?.(data);
	}, []);

	const handleReset = useCallback(() => {
		if (activeSessionId && terminalRefs.current[activeSessionId]) {
			terminalRefs.current[activeSessionId].current?.reset();
		}
	}, [activeSessionId]);

	if (!instanceId) {
		return (
			<section className="stack">
				<h1>Terminal</h1>
				<p className="muted">No instance ID provided.</p>
			</section>
		);
	}

	const sessionInfos = SESSIONS_TO_INFO(sessions);

	if (isMobile) {
		const activeSession = sessions.find((s) => s.id === activeSessionId);
		const status =
			terminalStatuses[activeSessionId ?? "default"] ?? "connecting";

		return (
			<section
				className={`terminal-page mobile ${isFullscreen ? "fullscreen" : ""}`}
			>
				{/* Overlay status bar — floats over terminal, never resizes it */}
				<div
					className={`mobile-terminal-overlay ${headerAutoHide.isVisible ? "visible" : "hidden"}`}
					onClick={(e) => e.stopPropagation()}
				>
					<div className="mobile-terminal-toolbar">
						<div className="mobile-terminal-toolbar-left">
							<button
								className="mobile-terminal-toolbtn"
								onClick={() => navigate(-1)}
								type="button"
								aria-label="Back"
							>
								<Icon name="arrow-left" size="sm" />
							</button>
						</div>
						<div className="mobile-terminal-toolbar-center">
							<span className="mobile-terminal-title">
								{activeSession?.name || "Terminal"}
							</span>
							<span
								className={`mobile-terminal-status status-dot ${status}`}
								aria-label={`Connection status: ${status}`}
							/>
						</div>
						<div className="mobile-terminal-toolbar-right">
							<button
								className="mobile-terminal-toolbtn"
								onClick={() => handleFontSizeChange(-1)}
								type="button"
								aria-label="Decrease font size"
							>
								<span style={{ fontSize: "0.75rem" }}>A-</span>
							</button>
							<button
								className="mobile-terminal-toolbtn"
								onClick={() => handleFontSizeChange(1)}
								type="button"
								aria-label="Increase font size"
							>
								<span style={{ fontSize: "1rem" }}>A+</span>
							</button>
							<button
								className="mobile-terminal-toolbtn"
								onClick={() => navigate(-1)}
								type="button"
								aria-label="Exit terminal"
							>
								<Icon name="close" size="sm" />
							</button>
						</div>
					</div>
					<div className="mobile-terminal-overlay-tabs">
						<TerminalSessionTabs
							sessions={sessionInfos}
							activeSessionId={activeSessionId ?? ""}
							onSelect={handleSelect}
							onClose={handleClose}
							onCreate={handleCreate}
							onRename={handleRename}
							isMobile={true}
						/>
					</div>
				</div>

				{/* Terminal content — always fills full viewport */}
				<div
					className="terminal-page-content mobile-full"
					style={{ paddingBottom: isKeyboardOpen ? keyboardHeight : 0 }}
					onClick={() => headerAutoHide.toggle()}
				>
					{error && <div className="terminal-error-banner">{error}</div>}
					{sessions
						.filter((session) => session.id === activeSessionId)
						.map((session) => (
							<div key={session.id} className="terminal-instance active">
								<TerminalComponent
									ref={terminalRefs.current[session.id]}
									instanceId={instanceId}
									sessionId={session.id}
									onClose={() => handleClose(session.id)}
									isMobile={true}
									showControls={false}
									activeModifier={activeModifier}
									onModifierChange={setActiveModifier}
									onTerminalReady={handleTerminalReady}
								/>
							</div>
						))}
					{sessions.length === 0 && !loading && (
						<div className="terminal-empty-state">
							<p>No terminal sessions. Press Alt+Shift+N to create one.</p>
						</div>
					)}
				</div>

				<SpecialKeysStrip
					onSend={handleSendKey}
					isVisible={!showSpecialKeysPanel}
					onMoreClick={() => setShowSpecialKeysPanel(true)}
					onKeepFocus={() => focusInputRef.current?.()}
					activeModifier={activeModifier}
					onModifierChange={setActiveModifier}
				/>

				<SpecialKeysPanel
					onSend={handleSendKey}
					isOpen={showSpecialKeysPanel}
					onClose={() => setShowSpecialKeysPanel(false)}
					onKeepFocus={() => focusInputRef.current?.()}
					activeModifier={activeModifier}
					onModifierChange={setActiveModifier}
				/>
			</section>
		);
	}

	return (
		<section
			className={`terminal-page ${isFullscreen ? "fullscreen" : ""}`}
			onClick={handleFullscreenClick}
		>
			{!isFullscreen && (
				<div className="terminal-page-header">
					<button
						className="secondary-button"
						onClick={() => navigate(-1)}
						type="button"
					>
						Back
					</button>
					<h1>Terminal</h1>
					<button
						className="secondary-button"
						onClick={() => setIsFullscreen((p) => !p)}
						type="button"
						title="Toggle fullscreen (Alt+Shift+F)"
					>
						{isFullscreen ? "Exit Fullscreen" : "Fullscreen"}
					</button>
				</div>
			)}
			{isFullscreen ? (
				<div className="terminal-fullscreen-header">
					<div className="terminal-fullscreen-header-tabs">
						<TerminalSessionTabs
							sessions={sessionInfos}
							activeSessionId={activeSessionId ?? ""}
							onSelect={handleSelect}
							onClose={handleClose}
							onCreate={handleCreate}
							onRename={handleRename}
							isMobile={false}
						/>
					</div>
					<div className="terminal-fullscreen-header-controls">
						<span
							className={`terminal-fullscreen-status status-dot ${terminalStatuses[activeSessionId ?? "default"] ?? "connecting"}`}
							aria-label={`Terminal status: ${terminalStatuses[activeSessionId ?? "default"] ?? "connecting"}`}
						/>
						<button
							className="terminal-header-button"
							onClick={() => handleFontSizeChange(-1)}
							type="button"
							aria-label="Decrease font size"
						>
							A-
						</button>
						<button
							className="terminal-header-button"
							onClick={() => handleFontSizeChange(1)}
							type="button"
							aria-label="Increase font size"
						>
							A+
						</button>
						<button
							className="terminal-header-button"
							onClick={() => setShowResetConfirm(true)}
							type="button"
							aria-label="Reset terminal"
						>
							Reset
						</button>
						<button
							className="terminal-close"
							onClick={() => setIsFullscreen(false)}
							type="button"
							title="Exit fullscreen (Esc)"
						>
							Exit
						</button>
					</div>
					{showResetConfirm && (
						<div className="terminal-reset-confirm">
							<div className="terminal-reset-confirm-content">
								<p>
									Reset terminal? This will kill the current shell session and
									start fresh.
								</p>
								<div className="terminal-reset-confirm-buttons">
									<button
										className="terminal-reset-confirm-button cancel"
										onClick={() => setShowResetConfirm(false)}
										type="button"
									>
										Cancel
									</button>
									<button
										className="terminal-reset-confirm-button confirm"
										onClick={() => {
											setShowResetConfirm(false);
											handleReset();
										}}
										type="button"
									>
										Reset
									</button>
								</div>
							</div>
						</div>
					)}
				</div>
			) : (
				<TerminalSessionTabs
					sessions={sessionInfos}
					activeSessionId={activeSessionId ?? ""}
					onSelect={handleSelect}
					onClose={handleClose}
					onCreate={handleCreate}
					onRename={handleRename}
					isMobile={false}
				/>
			)}
			<div className="terminal-page-content">
				{error && <div className="terminal-error-banner">{error}</div>}
				{sessions
					.filter((session) => session.id === activeSessionId)
					.map((session) => (
						<div key={session.id} className="terminal-instance active">
							<TerminalComponent
								ref={terminalRefs.current[session.id]}
								instanceId={instanceId}
								sessionId={session.id}
								onClose={() => handleClose(session.id)}
								isMobile={false}
								showControls={!isFullscreen}
								onTerminalReady={handleTerminalReady}
							/>
						</div>
					))}
				{sessions.length === 0 && !loading && (
					<div className="terminal-empty-state">
						<p>No terminal sessions. Press Alt+Shift+N to create one.</p>
					</div>
				)}
			</div>
		</section>
	);
};
