<script lang="ts">
	import { T } from "@threlte/core";
	import { interactivity } from "@threlte/extras";
	import { VideoTexture, CanvasTexture, LinearFilter, RGBAFormat, Shape, Path, ExtrudeGeometry, BoxGeometry } from "three";
	import { onMount } from "svelte";
	import type { VideoInstance } from "$lib/elements/video//VideoManager.svelte";
	import { videoManager } from "$lib/elements/video//VideoManager.svelte";

	// Props interface
	interface Props {
		// Video instance (required)
		videoInstance: VideoInstance;
		
		// Workspace ID (required for API calls)
		workspaceId: string;

		// TV dimensions
		width?: number;
		height?: number;
		depth?: number;
		frameThickness?: number;
		cornerRadius?: number;

		// Video props - now optional since we use the connection system
		videoSrc?: string;
		loadingDelay?: number;

		// Style props
		frameColor?: string;
		frameActiveColor?: string;
		frameMetalness?: number;
		frameRoughness?: number;
		frameEnvMapIntensity?: number;
		fallbackColor?: string;

		// Loading props
		showLoading?: boolean;
		loadingText?: string;
		loadingEmoji?: string;

		// Stream management props
		lazyLoad?: boolean;
	}

	// Props with defaults
	let {
		videoInstance,
		workspaceId,

		width = 4,
		height = 2.25,
		depth = 0.1,
		frameThickness = 0.2,
		cornerRadius = 0.15,

		videoSrc = "/video.mp4", // Fallback if no stream
		loadingDelay = 1000, // Reduced delay for real streams

		frameColor = "#374151",
		frameActiveColor = "#FFD700",
		frameMetalness = 0.05,
		frameRoughness = 0.4,
		frameEnvMapIntensity = 0.3,
		fallbackColor = "#FFD700",

		showLoading = true,
		loadingText = "Hover to load video...",
		loadingEmoji = "📺",

		lazyLoad = true
	}: Props = $props();

	let isPlayingLocked = $state(false);
	let isHovered = $state(false);

	// Video setup
	let videoElement: HTMLVideoElement | null = $state(null);
	let videoTexture: VideoTexture | null = $state(null);
	let loadingTexture: CanvasTexture | null = $state(null);
	let videoLoaded = $state(false);
	let isLoading = $state(false);

	// Function to create loading texture with text
	const createLoadingTexture = (text: string, backgroundColor: string = fallbackColor) => {
		const canvas = document.createElement('canvas');
		const ctx = canvas.getContext('2d')!;
		
		// Set canvas size (should match video aspect ratio)
		canvas.width = 512;
		canvas.height = 288; // 16:9 aspect ratio
		
		// Fill background
		ctx.fillStyle = backgroundColor;
		ctx.fillRect(0, 0, canvas.width, canvas.height);
		
		// Setup text styling
		ctx.fillStyle = '#FFFFFF';
		ctx.textAlign = 'center';
		ctx.textBaseline = 'middle';
		ctx.font = 'bold 32px Arial, sans-serif';
		
		// Add text shadow for better readability
		ctx.shadowColor = 'rgba(0, 0, 0, 0.7)';
		ctx.shadowBlur = 4;
		ctx.shadowOffsetX = 2;
		ctx.shadowOffsetY = 2;
		
		// Draw the loading text
		ctx.fillText(text, canvas.width / 2, canvas.height / 2);
		
		// Draw emoji if provided
		if (loadingEmoji) {
			ctx.font = 'bold 48px Arial, sans-serif';
			ctx.shadowColor = 'transparent';
			ctx.fillText(loadingEmoji, canvas.width / 2, canvas.height / 2 - 60);
		}
		
		// Create and return Three.js texture
		const texture = new CanvasTexture(canvas);
		texture.minFilter = LinearFilter;
		texture.magFilter = LinearFilter;
		texture.needsUpdate = true;
		
		return texture;
	};

	// Function to setup video element
	const setupVideo = (stream?: MediaStream) => {
		// Create video element
		videoElement = document.createElement("video");
		videoElement.loop = true;
		videoElement.muted = true; // Required for autoplay
		videoElement.playsInline = true;
		videoElement.crossOrigin = "anonymous";

		if (stream) {
			// Use live stream
			videoElement.srcObject = stream;
		} else {
			// Fallback to static video
			videoElement.src = videoSrc;
		}

		// Create video texture
		if (!videoTexture) {
			videoTexture = new VideoTexture(videoElement);
			videoTexture.minFilter = LinearFilter;
			videoTexture.magFilter = LinearFilter;
			videoTexture.format = RGBAFormat;
			videoTexture.flipY = true; // Fix the flipped video
			videoTexture.needsUpdate = true;

			videoElement.addEventListener("loadeddata", () => {
				videoLoaded = true;
				isLoading = false;
			});

			videoElement.addEventListener("canplay", () => {
				videoElement?.play().catch(console.error);
			});

			videoElement.addEventListener("error", (e) => {
				console.error("Video error:", e);
				isLoading = false;
			});

			if (stream) {
				// For streams, try to play immediately
				videoElement.play().catch(console.error);
				// Mark as loaded faster for live streams
				setTimeout(() => {
					if (!videoLoaded) {
						videoLoaded = true;
						isLoading = false;
					}
				}, 500);
			} else {
				videoElement.load();
			}
		}
	};

	// Function to cleanup video
	const cleanupVideo = () => {
		if (videoElement) {
			videoElement.pause();
			if (videoElement.srcObject) {
				videoElement.srcObject = null;
			}
			if (videoElement.src) {
				videoElement.removeAttribute("src");
				videoElement.load(); // This clears any pending loads
			}
			videoElement = null;
		}
		if (videoTexture) {
			videoTexture.dispose();
			videoTexture = null;
		}
		if (loadingTexture) {
			loadingTexture.dispose();
			loadingTexture = null;
		}
		videoLoaded = false;
		isLoading = false;
	};

	// Create loading texture on mount
	onMount(() => {
		loadingTexture = createLoadingTexture(loadingText);
	});

	let shouldPlay = $derived(isPlayingLocked || isHovered);

	// Watch for hover state changes
	$effect(() => {
		if (shouldPlay) {
			// Start loading video when hovered
			isLoading = true;

			// Create a loading texture for the loading state if not already created
			if (!loadingTexture) {
				loadingTexture = createLoadingTexture("Loading...");
			}

			// Cache status values to prevent reactive loops
			const canActivate = lazyLoad && (videoInstance.input.connectionState === 'prepared' || videoInstance.input.connectionState === 'paused');
			const hasPreparedRoom = videoInstance.input.preparedRoomId !== null;

			// Add a small delay to avoid loading on quick hovers
			const timeoutId = setTimeout(async () => {
				if (shouldPlay) {
					// Double check we're still hovered
					// Handle lazy loading: activate prepared/paused remote streams
					if (canActivate && hasPreparedRoom) {
						try {
							const result = await videoManager.activateRemoteStream(videoInstance.id, workspaceId);
							if (!result.success) {
								console.error("Failed to activate remote stream:", result.error);
								isLoading = false;
								return;
							}
						} catch (error) {
							console.error("Error activating remote stream:", error);
							isLoading = false;
							return;
						}
					}

					const stream = videoInstance.currentStream;
					setupVideo(stream || undefined);
				}
			}, loadingDelay);

			return () => {
				clearTimeout(timeoutId);
			};
		} else {
			// Handle cleanup when not hovered
			// Cleanup video rendering
			cleanupVideo();

			// Cache status values to prevent reactive loops
			const canPause = lazyLoad && videoInstance.input.connectionState === 'connected' && videoInstance.input.connectionPolicy === 'lazy';

			// Only pause remote streams with lazy policy (not persistent connections)
			if (canPause) {
				try {
					videoManager.pauseRemoteStream(videoInstance.id);
				} catch (error) {
					console.error("Error pausing remote stream:", error);
				}
			}
		}
	});

	// Watch for stream changes when video is active (hovered) - simplified to prevent loops
	let lastStreamRef: MediaStream | null = null;
	$effect(() => {
		if (!shouldPlay || !videoElement) {
			lastStreamRef = null;
			return;
		}

		const currentStream = videoInstance.currentStream;
		
		// Only update if the stream reference has actually changed
		if (currentStream !== lastStreamRef) {
			lastStreamRef = currentStream;
			
			// Gracefully stop current video
			try {
				videoElement.pause();

				// Clear sources
				if (videoElement.srcObject) {
					videoElement.srcObject = null;
				}
				if (videoElement.src) {
					videoElement.removeAttribute("src");
					videoElement.load(); // This clears any pending loads
				}
			} catch (error) {
				console.warn("Error stopping previous video:", error);
			}

			// Dispose current texture
			if (videoTexture) {
				videoTexture.dispose();
				videoTexture = null;
			}

			// Create/update loading texture for the loading state
			if (loadingTexture) {
				loadingTexture.dispose();
			}
			loadingTexture = createLoadingTexture("Loading...");

			// Reset state
			videoLoaded = false;
			isLoading = true;

			// Small delay to ensure cleanup is complete
			setTimeout(() => {
				if (shouldPlay && currentStream === lastStreamRef) {
					// Make sure we're still hovered and stream hasn't changed
					setupVideo(currentStream || undefined);
				}
			}, 50);
		}
	});

	// Create the TV frame geometry (outer rounded rectangle)
	function createTVFrame(
		tvWidth: number,
		tvHeight: number,
		tvDepth: number,
		tvFrameThickness: number,
		tvCornerRadius: number
	) {
		const shape = new Shape();
		const x = -tvWidth / 2;
		const y = -tvHeight / 2;
		const w = tvWidth;
		const h = tvHeight;
		const radius = tvCornerRadius;

		shape.moveTo(x, y + radius);
		shape.lineTo(x, y + h - radius);
		shape.quadraticCurveTo(x, y + h, x + radius, y + h);
		shape.lineTo(x + w - radius, y + h);
		shape.quadraticCurveTo(x + w, y + h, x + w, y + h - radius);
		shape.lineTo(x + w, y + radius);
		shape.quadraticCurveTo(x + w, y, x + w - radius, y);
		shape.lineTo(x + radius, y);
		shape.quadraticCurveTo(x, y, x, y + radius);

		// Create hole for screen (inner rectangle)
		const hole = new Path();
		const hx = x + tvFrameThickness;
		const hy = y + tvFrameThickness;
		const hwidth = w - tvFrameThickness * 2;
		const hheight = h - tvFrameThickness * 2;
		const hradius = tvCornerRadius * 0.5;

		hole.moveTo(hx, hy + hradius);
		hole.lineTo(hx, hy + hheight - hradius);
		hole.quadraticCurveTo(hx, hy + hheight, hx + hradius, hy + hheight);
		hole.lineTo(hx + hwidth - hradius, hy + hheight);
		hole.quadraticCurveTo(hx + hwidth, hy + hheight, hx + hwidth, hy + hheight - hradius);
		hole.lineTo(hx + hwidth, hy + hradius);
		hole.quadraticCurveTo(hx + hwidth, hy, hx + hwidth - hradius, hy);
		hole.lineTo(hx + hradius, hy);
		hole.quadraticCurveTo(hx, hy, hx, hy + hradius);

		shape.holes.push(hole);

		return new ExtrudeGeometry(shape, {
			depth: tvDepth,
			bevelEnabled: true,
			bevelThickness: 0.02,
			bevelSize: 0.02,
			bevelSegments: 8
		});
	}

	// Create the screen (video display area)
	function createScreen(tvWidth: number, tvHeight: number, tvFrameThickness: number) {
		const w = tvWidth - tvFrameThickness * 2;
		const h = tvHeight - tvFrameThickness * 2;

		// Create a very thin box for the screen area (only visible from front)
		return new BoxGeometry(w, h, 0.02);
	}

	const frameGeometry = createTVFrame(width, height, depth, frameThickness, cornerRadius);
	const screenGeometry = createScreen(width, height, frameThickness);

	interactivity();
</script>

<T.Group
	onclick={() => (isPlayingLocked = !isPlayingLocked)}
	onpointerenter={() => (isHovered = true)}
	onpointerleave={() => (isHovered = false)}
>
	<!-- TV Frame -->
	<T.Mesh geometry={frameGeometry}>
		<T.MeshStandardMaterial
			color={shouldPlay ? frameActiveColor : frameColor}
			metalness={frameMetalness}
			roughness={frameRoughness}
			envMapIntensity={frameEnvMapIntensity}
		/>
	</T.Mesh>

	<T.Mesh geometry={screenGeometry} position.z={depth / 2 - 0.01}>
		{#if videoLoaded && videoTexture}
			<T.MeshBasicMaterial map={videoTexture} />
		{:else if loadingTexture}
			<T.MeshBasicMaterial map={loadingTexture} />
		{:else}
			<T.MeshBasicMaterial color={fallbackColor} />
		{/if}
	</T.Mesh>

	<!-- Loading/Hover State Overlay -->
	{#if showLoading && (!isHovered || isLoading)}
		<T.MeshBasicMaterial color={fallbackColor} />
	{/if}

	<!-- Video Screen -->
	<!-- <T.Mesh position.z={depth / 2 - 0.01} scale={[2, 2, 2]} position.y={0.5}>

		{#if videoLoaded && videoTexture}
			{#if videoSrc}
				<T.MeshBasicMaterial map={videoTexture} />
				<Root>
					<Container >
						<Video bind:element={videoElement} autoplay muted width={100} height={100} />
						<Text text={videoElement?.src} />
					</Container>
				</Root>
			{:else}
				<T.MeshBasicMaterial color={fallbackColor} />
			{/if}
		{:else}
			<T.MeshBasicMaterial color={fallbackColor} />
		{/if}
	</T.Mesh> -->
</T.Group>
