<script lang="ts">
	import { onMount } from "svelte";
	import { themeColors } from "$lib/stores/theme";
	import {
		terminalSettings,
		type SpeedPreset,
		speedPresets,
	} from "$lib/config";
	import TuiHeader from "./tui/TuiHeader.svelte";
	import TuiBody from "./tui/TuiBody.svelte";
	import TuiFooter from "./tui/TuiFooter.svelte";
	import "$lib/assets/css/terminal-tui.css";

	// Import extracted modules
	import { parseLine, createTerminalAPI } from "./tui/terminal-api";
	import {
		runTypingAnimation,
		skipTypingAnimation,
	} from "./tui/terminal-typing";
	import {
		createKeyboardHandler,
		handleNavigation,
		scrollToHash,
	} from "./tui/terminal-keyboard";

	import type {
		TerminalLine,
		ParsedLine,
		DisplayedLine,
		TerminalAPI,
	} from "./tui/types";

	interface Props {
		lines?: TerminalLine[];
		title?: string;
		class?: string;
		onComplete?: () => void;
		interactive?: boolean;
		speed?: SpeedPreset | number;
		autoscroll?: boolean;
		terminal?: TerminalAPI;
	}

	let {
		lines = $bindable([]),
		title = "terminal",
		class: className = "",
		onComplete,
		interactive = true,
		speed = "normal",
		autoscroll = true,
		terminal = $bindable(),
	}: Props = $props();

	// Calculate speed multiplier from preset or number
	const speedMultiplier = $derived(
		typeof speed === "number" ? speed : (speedPresets[speed] ?? 1),
	);

	// Get colorMap from current theme
	const colorMap = $derived($themeColors.colorMap);

	// Pre-parse all lines upfront (segments + plain text)
	const parsedLines = $derived<ParsedLine[]>(
		lines.map((line) => parseLine(line, colorMap)),
	);

	let displayedLines = $state<DisplayedLine[]>([]);
	let currentLineIndex = $state(0);
	let isTyping = $state(false);
	let isComplete = $state(false);
	let selectedIndex = $state(-1);
	let skipRequested = $state(false);
	let terminalElement: HTMLDivElement;
	let bodyElement = $state<HTMLDivElement>();

	// Track colorMap identity to detect theme/mode changes
	let lastColorMapId = $state("");

	// When colorMap changes (theme/mode toggle), update displayedLines with new parsed segments
	$effect(() => {
		// Create a simple identity string from a few colorMap values
		const colorMapId = `${colorMap.red}-${colorMap.text}-${colorMap.primary}`;

		// Only update if colorMap actually changed and animation is complete
		if (
			colorMapId !== lastColorMapId &&
			isComplete &&
			displayedLines.length > 0
		) {
			// Store current showImage states before updating
			const showImageStates = displayedLines.map((d) => d.showImage);

			// Update with new parsed content
			displayedLines = parsedLines.map((parsed, i) => ({
				parsed,
				charIndex: parsed.plainText.length,
				complete: true,
				showImage: showImageStates[i] ?? parsed.line.type === "image",
			}));
		}

		lastColorMapId = colorMapId;
	});

	// Helper to check if a line or its children contain buttons
	function hasButtons(line: TerminalLine): boolean {
		if (line.type === "button") return true;
		if (line.type === "group" && line.children) {
			return line.children.some((child) => hasButtons(child));
		}
		return false;
	}

	// Get all interactive button indices (including buttons nested in groups)
	const buttonIndices = $derived(
		displayedLines
			.map((item, i) => (hasButtons(item.parsed.line) ? i : -1))
			.filter((i) => i !== -1),
	);

	// ========================================================================
	// TYPING ANIMATION
	// ========================================================================

	function typeText() {
		runTypingAnimation({
			parsedLines,
			speedMultiplier,
			terminalSettings,
			buttonIndices,
			interactive,
			autoscroll,
			getBodyElement: () => bodyElement,
			getState: () => ({
				displayedLines,
				currentLineIndex,
				isTyping,
				isComplete,
				skipRequested,
				selectedIndex,
			}),
			setState: (updates) => {
				if (updates.displayedLines !== undefined)
					displayedLines = updates.displayedLines;
				if (updates.currentLineIndex !== undefined)
					currentLineIndex = updates.currentLineIndex;
				if (updates.isTyping !== undefined) isTyping = updates.isTyping;
				if (updates.isComplete !== undefined)
					isComplete = updates.isComplete;
				if (updates.skipRequested !== undefined)
					skipRequested = updates.skipRequested;
				if (updates.selectedIndex !== undefined)
					selectedIndex = updates.selectedIndex;
			},
			onComplete,
			scrollToHash: () => scrollToHash(bodyElement),
		});
	}

	function skipAnimation() {
		skipTypingAnimation(
			parsedLines,
			buttonIndices,
			interactive,
			() => ({
				displayedLines,
				currentLineIndex,
				isTyping,
				isComplete,
				skipRequested,
				selectedIndex,
			}),
			(updates) => {
				if (updates.displayedLines !== undefined)
					displayedLines = updates.displayedLines;
				if (updates.currentLineIndex !== undefined)
					currentLineIndex = updates.currentLineIndex;
				if (updates.isTyping !== undefined) isTyping = updates.isTyping;
				if (updates.isComplete !== undefined)
					isComplete = updates.isComplete;
				if (updates.skipRequested !== undefined)
					skipRequested = updates.skipRequested;
				if (updates.selectedIndex !== undefined)
					selectedIndex = updates.selectedIndex;
			},
			() => bodyElement,
			autoscroll,
			onComplete,
		);
	}

	// ========================================================================
	// TERMINAL API
	// ========================================================================

	terminal = createTerminalAPI({
		getState: () => ({
			lines,
			displayedLines,
			isTyping,
			isComplete,
			currentLineIndex,
			selectedIndex,
			skipRequested,
		}),
		setState: (updates) => {
			if (updates.lines !== undefined) lines = updates.lines;
			if (updates.displayedLines !== undefined)
				displayedLines = updates.displayedLines;
			if (updates.isTyping !== undefined) isTyping = updates.isTyping;
			if (updates.isComplete !== undefined)
				isComplete = updates.isComplete;
			if (updates.currentLineIndex !== undefined)
				currentLineIndex = updates.currentLineIndex;
			if (updates.selectedIndex !== undefined)
				selectedIndex = updates.selectedIndex;
			if (updates.skipRequested !== undefined)
				skipRequested = updates.skipRequested;
		},
		getColorMap: () => colorMap,
		getBodyElement: () => bodyElement,
		typeText,
	});

	// ========================================================================
	// KEYBOARD HANDLING
	// ========================================================================

	const handleKeydown = createKeyboardHandler({
		getIsTyping: () => isTyping,
		getIsComplete: () => isComplete,
		getInteractive: () => interactive,
		getButtonIndices: () => buttonIndices,
		getSelectedIndex: () => selectedIndex,
		setSelectedIndex: (index) => {
			selectedIndex = index;
		},
		getDisplayedLines: () => displayedLines,
		getBodyElement: () => bodyElement,
		skipAnimation,
		scrollMargin: terminalSettings.scrollMargin,
	});

	function handleButtonClick(index: number, line?: TerminalLine) {
		if (line) {
			// If line is provided (e.g. from nested group), use it directly
			// We don't update selectedIndex because it might not map to a top-level line
			handleNavigation(line as any);
		} else {
			// Fallback to top-level index lookup
			selectedIndex = index;
			handleNavigation(displayedLines[index]?.parsed.line as any);
		}
	}

	function handleLinkClick(index: number) {
		handleNavigation(displayedLines[index]?.parsed.line as any);
	}

	onMount(() => {
		typeText();
	});
</script>

<svelte:window on:keydown={handleKeydown} />

<div
	class="tui-terminal {className}"
	style="
		--terminal-bg: {$themeColors.terminal};
		--terminal-text: {$themeColors.text};
		--terminal-muted: {$themeColors.textMuted};
		--terminal-border: {$themeColors.border};
		--terminal-prompt: {$themeColors.terminalPrompt};
		--terminal-user: {$themeColors.terminalUser};
		--terminal-path: {$themeColors.terminalPath};
		--terminal-primary: {$themeColors.primary};
		--terminal-secondary: {$themeColors.secondary};
		--terminal-accent: {$themeColors.accent};
		--terminal-bg-light: {$themeColors.backgroundLight};
	"
	bind:this={terminalElement}
	role="region"
	aria-label="Terminal interface"
>
	<!-- Hyprland-style border glow -->
	<div class="tui-border-glow"></div>

	<!-- TUI Content -->
	<div class="tui-content">
		<TuiHeader
			{title}
			{interactive}
			hasButtons={buttonIndices.length > 0}
		/>

		<!-- Main terminal area -->
		<TuiBody
			bind:ref={bodyElement}
			{displayedLines}
			{currentLineIndex}
			{isTyping}
			{selectedIndex}
			onButtonClick={handleButtonClick}
			onHoverButton={(i) => (selectedIndex = i)}
			onLinkClick={handleLinkClick}
			{terminalSettings}
		/>

		<TuiFooter
			{isTyping}
			linesCount={displayedLines.length}
			{skipAnimation}
		/>
	</div>
</div>
