<script lang="ts">
	import Toolbar from './components/Toolbar.svelte';
	import Container from './components/Container.svelte';
	import VirtualCursor from './components/VirtualCursor.svelte';
	import SelectDropdown from './components/SelectDropdown.svelte';
	import ContextMenu from './components/ContextMenu.svelte';
	import type { BrowserSelectInfo, BrowserContextMenuInfo } from '$frontend/utils/native-ui';
	import { fly } from 'svelte/transition';
	import { cubicOut } from 'svelte/easing';
	import { onMount, onDestroy } from 'svelte';
	import type { DeviceSize, Rotation } from '$frontend/utils/preview-constants';
	import { debug } from '$shared/utils/logger';
	import { createBrowserCoordinator } from './core/coordinator.svelte';
	import { sendScaleUpdate } from './core/interactions.svelte';
	import { projectState } from '$frontend/stores/core/projects.svelte';
	import { appState } from '$frontend/stores/core/app.svelte';

	let {
		url = $bindable(''),
		isOpen = $bindable(false),
		mode = $bindable<'split' | 'tab'>('split'),
		deviceSize = $bindable<DeviceSize>('laptop'),
		rotation = $bindable<Rotation>('portrait'),
		previewDimensions = $bindable<any>({ scale: 1 })
	} = $props();

	// Get projectId from project state (REQUIRED for project isolation)
	const projectId = $derived(projectState.currentProject?.id || '');

	// URL input state - separate from actual preview URL
	let urlInput = $state('');

	// UI state
	let isLoading = $state(false);
	let isLaunchingBrowser = $state(false);
	let isNavigating = $state(false);
	let isReconnecting = $state(false); // True during fast reconnect after navigation
	let sessionId = $state<string | null>(null);
	let sessionInfo = $state<any>(null);
	let isConnected = $state(false);
	let isStreamReady = $state(false);
	let errorMessage = $state<string | null>(null);
	let isConsoleOpen = $state(false); // Console temporarily disabled
	let consoleLogs = $state<any[]>([]);

	// Virtual cursors
	let virtualCursor = $state<{x: number, y: number, visible: boolean, clicking?: boolean}>({
		x: 0, y: 0, visible: false, clicking: false
	});
	let mcpVirtualCursor = $state<{x: number, y: number, visible: boolean, clicking?: boolean}>({
		x: 0, y: 0, visible: false, clicking: false
	});

	// Native UI state
	let currentSelectInfo = $state<BrowserSelectInfo | null>(null);
	let currentContextMenuInfo = $state<BrowserContextMenuInfo | null>(null);

	// Canvas and preview state
	let canvasAPI = $state<any>(null);
	let currentTabLastFrameData = $state<any>(null);

	// Flag to prevent URL watcher from double-launching during MCP session creation
	const mcpLaunchInProgress = $state(false);

	// Touch interaction mode for canvas
	let touchMode = $state<'scroll' | 'cursor'>('scroll');

	// Create browser coordinator with projectId
	const coordinator = createBrowserCoordinator({
		projectId: () => projectId, // Pass projectId as getter function
		onUrlChange: (newUrl) => {
			url = newUrl;
		},
		onUrlInputChange: (newUrlInput) => {
			urlInput = newUrlInput;
		},
		onSessionChange: (newSessionId) => {
			sessionId = newSessionId;
		},
		onLoadingChange: (loading) => {
			isLoading = loading;
		},
		onErrorChange: (error) => {
			errorMessage = error;
		},
		onSelectOpen: (selectInfo) => {
			currentSelectInfo = selectInfo;
		},
		onContextMenuOpen: (menuInfo) => {
			currentContextMenuInfo = menuInfo;
		},
		onVirtualCursorUpdate: (x, y, clicking) => {
			virtualCursor = { x, y, visible: true, clicking: clicking || false };
			if (clicking) {
				setTimeout(() => {
					virtualCursor = { ...virtualCursor, clicking: false };
				}, 300);
			}
		},
		onVirtualCursorHide: () => {
			virtualCursor = { ...virtualCursor, visible: false };
		},
		onMcpCursorUpdate: (x, y, clicking) => {
			mcpVirtualCursor = { x, y, visible: true, clicking: clicking || false };
			if (clicking) {
				setTimeout(() => {
					mcpVirtualCursor = { ...mcpVirtualCursor, clicking: false };
				}, 300);
			}
		},
		onMcpCursorHide: () => {
			mcpVirtualCursor = { ...mcpVirtualCursor, visible: false };
		},
		transformBrowserToDisplayCoordinates: (browserX, browserY) => {
			return transformBrowserToDisplayCoordinates(browserX, browserY);
		}
	});

	const { tabManager, streamHandler, nativeUIHandler, mcpHandler } = coordinator;

	// Derived states from tab manager
	const tabs = $derived(tabManager.tabs);
	const activeTabId = $derived(tabManager.activeTabId);
	const activeTab = $derived(tabManager.activeTab);

	// Initialize event listeners. Tab recovery itself is driven by the
	// preview-tabs dock's load() inside the workspace switch barrier — we just
	// adopt the singleton state below.
	onMount(() => {
		coordinator.initialize();
	});

	// Cleanup
	onDestroy(async () => {
		await coordinator.cleanup();
	});

	// Watch for project changes and reload sessions.
	//
	// Bail while a workspace switch is in flight: the singleton tabManager is
	// mid clear/restore/load during that window and adopting it now would race
	// with the dock — observed as a spurious "New Tab" or duplicated/cross-
	// project tabs once load() completes. Once the barrier drops, isSwitching
	// flips and the effect re-runs against the authoritative state.
	let previousProjectId = '';
	let initialRecoveryDone = false;
	$effect(() => {
		const currentProjectId = projectId;
		if (appState.isSwitching) return;

		// Initial recovery - trigger when projectId first becomes available after mount
		if (!initialRecoveryDone && currentProjectId && previousProjectId === '') {
			debug.log('preview', `🔄 Initial project loaded: ${currentProjectId}, adopting recovered tabs`);
			previousProjectId = currentProjectId;
			initialRecoveryDone = true;
			coordinator.switchProject();
			return;
		}

		// Project changed - switch to new project
		if (currentProjectId && currentProjectId !== previousProjectId && previousProjectId !== '') {
			debug.log('preview', `🔄 Project changed: ${previousProjectId} → ${currentProjectId}`);
			previousProjectId = currentProjectId;
			coordinator.switchProject();
		}
	});

	// Sync state from active tab
	$effect(() => {
		const tab = activeTab;
		if (tab) {
			url = tab.url;
			urlInput = tab.url;
			sessionId = tab.sessionId;
			sessionInfo = tab.sessionInfo;
			isConnected = tab.isConnected;
			isStreamReady = tab.isStreamReady;
			isLoading = tab.isLoading;
			isLaunchingBrowser = tab.isLaunchingBrowser;
			isNavigating = tab.isNavigating;
			errorMessage = tab.errorMessage;
			deviceSize = tab.deviceSize;
			rotation = tab.rotation;
			consoleLogs = tab.consoleLogs;
			canvasAPI = tab.canvasAPI;
			previewDimensions = tab.previewDimensions || { scale: 1 };
			currentTabLastFrameData = tab.lastFrameData;

			// Setup canvas after tab switch
			if (canvasAPI && canvasAPI.setupCanvas) {
				setTimeout(() => {
					canvasAPI.setupCanvas();
				}, 50);
			}
		}
	});

	// Watch for URL changes from parent (e.g., MCP integration)
	let previousUrl = '';
	$effect(() => {
		if (!url || url === previousUrl) return;
		// Ignore browser-internal error pages (e.g. DNS failure) — they are not real URLs
		// and should never trigger a new navigation attempt.
		if (url.startsWith('chrome-error://') || url.startsWith('chrome://')) {
			previousUrl = url;
			return;
		}
		if (mcpLaunchInProgress) {
			previousUrl = url;
			urlInput = url;
			return;
		}

		debug.log('preview', `📝 URL changed from parent: ${previousUrl} → ${url}`);
		previousUrl = url;
		urlInput = url;

		if (activeTabId) {
			const tab = activeTab;
			if (tab) {
				// Prevent duplicate launches - check if already launching or has session for this URL
				if (tab.isLaunchingBrowser) {
					debug.log('preview', `⏭️ Skipping launch - already launching browser for ${activeTabId}`);
					return;
				}

				// If URL hasn't changed from tab's URL, don't re-launch
				if (tab.url === url && tab.sessionId) {
					debug.log('preview', `⏭️ Skipping launch - URL unchanged and session exists`);
					return;
				}

				tabManager.updateTab(activeTabId, {
					url: url,
					errorMessage: null
				});

				if (tab.sessionId) {
					coordinator.navigateBrowserForTab(activeTabId, url);
				} else {
					coordinator.launchBrowserForTab(activeTabId, url);
				}
			}
		} else {
			coordinator.createNewTab(url);
		}
	});

	// Store canvasAPI and previewDimensions in active tab
	$effect(() => {
		if (activeTabId && activeTab) {
			const updates: any = {};
			let needsUpdate = false;

			if (canvasAPI && activeTab.canvasAPI !== canvasAPI) {
				updates.canvasAPI = canvasAPI;
				needsUpdate = true;
			}

			if (previewDimensions && JSON.stringify(activeTab.previewDimensions) !== JSON.stringify(previewDimensions)) {
				updates.previewDimensions = previewDimensions;
				needsUpdate = true;
			}

			if (needsUpdate) {
				tabManager.updateTab(activeTabId, updates);
			}
		}
	});

	// Sync isStreamReady back to active tab
	$effect(() => {
		if (activeTabId && activeTab && activeTab.isStreamReady !== isStreamReady) {
			tabManager.updateTab(activeTabId, { isStreamReady });
		}
	});

	// Sync isNavigating back to active tab (Canvas resets this on first frame after navigation)
	$effect(() => {
		if (activeTabId && activeTab && activeTab.isNavigating !== isNavigating) {
			tabManager.updateTab(activeTabId, { isNavigating });
		}
	});

	// Watch scale changes and send to backend
	let lastSentScale = 1;
	$effect(() => {
		const currentScale = previewDimensions?.scale || 1;
		if (currentScale !== lastSentScale && sessionId && isStreamReady) {
			debug.log('preview', `📐 Scale changed to ${currentScale}, sending to backend`);
			sendScaleUpdate(currentScale);
			lastSentScale = currentScale;
		}
	});

	// Initialize URL input
	$effect(() => {
		if (url && !url.startsWith('http://') && !url.startsWith('https://') && !url.startsWith('file://')) {
			url = 'http://' + url;
		}
		if (url && !urlInput) {
			urlInput = url;
		}
	});

	// URL handling functions
	function handleGoClick() {
		if (!urlInput.trim()) return;

		let processedUrl = urlInput.trim();
		if (!processedUrl.startsWith('http://') && !processedUrl.startsWith('https://') && !processedUrl.startsWith('file://')) {
			processedUrl = 'http://' + processedUrl;
		}

		if (!activeTabId) {
			coordinator.createNewTab(processedUrl);
		} else {
			const tab = activeTab;
			if (tab) {
				tabManager.updateTab(activeTabId, {
					url: processedUrl,
					errorMessage: null
				});

				if (tab.sessionId) {
					coordinator.navigateBrowserForTab(activeTabId, processedUrl);
				} else {
					coordinator.launchBrowserForTab(activeTabId, processedUrl);
				}
			}
		}
	}

	function refreshPreview() {
		if (!activeTabId) return;
		const tab = activeTab;
		if (tab && tab.sessionId && tab.url) {
			coordinator.navigateBrowserForTab(activeTabId, tab.url);
		}
	}

	async function closePreview() {
		if (activeTabId && tabs.length > 0) {
			coordinator.closeTab(activeTabId);
			if (tabs.length === 0) {
				isOpen = false;
			}
		} else {
			isOpen = false;
		}
	}

	function handleCanvasInteraction(action: any) {
		const tab = activeTab;
		if (tab && tab.sessionId) {
			// Hide the AI cursor instantly if the human explicitly interacts
			mcpVirtualCursor = { ...mcpVirtualCursor, visible: false };	
			coordinator.sendInteraction(action);
		}
	}

	function transformBrowserToDisplayCoordinates(browserX: number, browserY: number): { x: number, y: number } | null {
		let canvasElement: HTMLCanvasElement | null = null;
		if (canvasAPI && canvasAPI.getCanvasElement) {
			canvasElement = canvasAPI.getCanvasElement();
		}
		if (!canvasElement && typeof document !== 'undefined') {
			canvasElement = document.querySelector('canvas[tabindex="0"]') as HTMLCanvasElement;
		}
		if (!canvasElement) return null;

		try {
			const canvasRect = canvasElement.getBoundingClientRect();
			if (!canvasRect || canvasElement.width === 0 || canvasElement.height === 0) return null;
			
			const scaleX = canvasRect.width / canvasElement.width;
			const scaleY = canvasRect.height / canvasElement.height;
			const screenX = canvasRect.left + (browserX * scaleX);
			const screenY = canvasRect.top + (browserY * scaleY);

			return { x: screenX, y: screenY };
		} catch (e) {
			return null;
		}
	}

	async function handleSelectOption(selectedIndex: number) {
		if (!currentSelectInfo) return;
		await nativeUIHandler.respondSelectOption(currentSelectInfo, selectedIndex);
		currentSelectInfo = null;
	}

	function closeSelectDropdown() {
		currentSelectInfo = null;
	}

	async function handleContextMenuItem(itemId: string) {
		if (!currentContextMenuInfo) return;
		const menuInfo = currentContextMenuInfo;
		currentContextMenuInfo = null;
		await nativeUIHandler.respondContextMenuItem(menuInfo, itemId);
	}

	function closeContextMenu() {
		currentContextMenuInfo = null;
	}

	function isCurrentTabMcpControlled(): boolean {
		return mcpHandler.isCurrentTabMcpControlled();
	}

	// Hide MCP virtual cursor when switching to a non-MCP-controlled tab
	$effect(() => {
		void activeTabId; // track activeTabId changes
		if (!isCurrentTabMcpControlled()) {
			mcpVirtualCursor = { x: 0, y: 0, visible: false, clicking: false };
		}
	});

	// Stream message handling
	$effect(() => {
		if (activeTabId && sessionId) {
			// Message handling is done via coordinator
		}
	});

	// Expose methods for parent (PreviewPanel)
	export const browserActions = {
		getTouchMode: () => touchMode,
		setTouchMode: (mode: 'scroll' | 'cursor') => { touchMode = mode; },
		changeDeviceSize: (size: DeviceSize) => {
			coordinator.changeDeviceSize(size, previewDimensions?.scale);
		},
		toggleRotation: () => {
			coordinator.toggleRotation(previewDimensions?.scale);
		},
		getSessionInfo: () => sessionInfo,
		getIsStreamReady: () => isStreamReady,
		getErrorMessage: () => errorMessage,
		getIsMcpControlled: () => isCurrentTabMcpControlled()
	};
</script>

{#if isOpen && mode === 'split'}
	<div
		class="h-full flex flex-col theme-transition bg-slate-50 dark:bg-slate-900 dot-pattern"
		in:fly={{ x: 300, duration: 300, easing: cubicOut }}
		out:fly={{ x: 300, duration: 250, easing: cubicOut }}
	>
		<!-- Preview Toolbar -->
		<Toolbar
			bind:url
			bind:urlInput
			bind:isLoading
			bind:isLaunchingBrowser
			bind:isNavigating
			bind:isReconnecting
			bind:sessionId
			bind:sessionInfo
			bind:isConnected
			bind:isStreamReady
			bind:errorMessage
			bind:isConsoleOpen
			{tabs}
			{activeTabId}
			mcpControlledTabIds={mcpHandler.getControlledTabIds()}
			onGoClick={handleGoClick}
			onRefresh={refreshPreview}
			onOpenInExternalBrowser={() => {}}
			onClosePreview={closePreview}
			onToggleConsole={() => {}}
			onUrlInput={() => {}}
			onUrlKeydown={() => {}}
			onSwitchTab={(tabId: string) => coordinator.switchToTab(tabId)}
			onCloseTab={(tabId: string) => coordinator.closeTab(tabId)}
			onNewTab={() => coordinator.createNewTab()}
		/>

		<!-- Preview Container -->
		<div class="flex-1 flex min-h-0">
			<Container
				projectId={projectId}
				bind:url
				bind:isLoading
				bind:isLaunchingBrowser
				bind:isNavigating
				bind:isReconnecting
				bind:deviceSize
				bind:rotation
				bind:sessionId
				bind:sessionInfo
				bind:isConnected
				bind:isStreamReady
				bind:errorMessage
				bind:virtualCursor
				bind:mcpVirtualCursor
				bind:canvasAPI
				bind:previewDimensions
				bind:lastFrameData={currentTabLastFrameData}
				bind:touchMode
				isMcpControlled={isCurrentTabMcpControlled()}
				onInteraction={handleCanvasInteraction}
				onRetry={handleGoClick}
			/>
		</div>

		<!-- Native UI Overlays -->
		<SelectDropdown
			bind:selectInfo={currentSelectInfo}
			onSelect={handleSelectOption}
			onClose={closeSelectDropdown}
		/>

		<ContextMenu
			bind:menuInfo={currentContextMenuInfo}
			onSelectItem={handleContextMenuItem}
			onClose={closeContextMenu}
		/>
	</div>
{/if}

<style>
	/* Dot Pattern Background */
	.dot-pattern {
		background: center center / 1.5rem repeat transparent;
		/* Light mode: darker dots */
		background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20width%3D'30'%20height%3D'30'%20fill%3D'none'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M1.227%200c.687%200%201.227.54%201.227%201.227s-.54%201.227-1.227%201.227S0%201.914%200%201.227.54%200%201.227%200z'%20fill%3D'rgba(0%2C0%2C0%2C0.25)'%2F%3E%3C%2Fsvg%3E");
	}

	/* Dark mode: lighter dots */
	:global(.dark) .dot-pattern {
		background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20width%3D'30'%20height%3D'30'%20fill%3D'none'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M1.227%200c.687%200%201.227.54%201.227%201.227s-.54%201.227-1.227%201.227S0%201.914%200%201.227.54%200%201.227%200z'%20fill%3D'rgba(255%2C255%2C255%2C0.15)'%2F%3E%3C%2Fsvg%3E");
	}
</style>
