<script lang="ts">
	import { onDestroy } from 'svelte';
	// Relative imports (not $lib) so the standalone Vite element bundle resolves
	// without needing the SvelteKit alias config.
	import type { VideoElement } from './types';
	import { parseEmbedUrl } from './utils/video-embed';
	import { createEmbedPlayer, type UnifiedPlayer } from './utils/embed-players';

	interface Props {
		element: VideoElement;
		/** When true, render our own play/pause/scrub overlay on top of the iframe.
		 * The iframe itself always has the provider's chrome hidden via URL params. */
		controlsOverlay?: boolean;
		/** Disable any pointer events on the iframe so the wrapper above can
		 * receive selection clicks. Used in the editor — never in /present. */
		passthroughClicks?: boolean;
		/** Some host components want a programmatic handle to the player so they
		 * can pause embeds when the active slide changes. Returned via this callback. */
		onReady?: (player: UnifiedPlayer) => void;
	}

	let { element, controlsOverlay = false, passthroughClicks = false, onReady }: Props = $props();

	let iframeRef: HTMLIFrameElement | null = $state(null);
	let player: UnifiedPlayer | null = $state(null);
	let isPlaying = $state(false);
	let isMuted = $state(true);
	let currentTime = $state(0);
	let duration = $state(0);

	const embed = $derived(parseEmbedUrl(element.src, {
		autoplay: element.autoplay,
		muted: element.muted,
		loop: element.loop,
		startTime: element.startTime,
		endTime: element.endTime,
		showControls: false // we always hide native chrome and render our own when requested
	}));

	let unsubState: (() => void) | null = null;
	let pollInterval: ReturnType<typeof setInterval> | null = null;

	$effect(() => {
		// Spin up a player whenever the iframe element + a valid embed URL exist
		// together. The effect re-runs if the embed URL changes (different video)
		// — we tear down the old player first.
		if (!iframeRef || !embed) return;
		let cancelled = false;
		const ifr = iframeRef;
		const e = embed;

		(async () => {
			const p = await createEmbedPlayer({ provider: e.provider, iframe: ifr, videoId: e.id, startTime: element.startTime, muted: element.muted });
			if (cancelled) { p.destroy(); return; }
			player = p;
			isMuted = element.muted;
			onReady?.(p);

			unsubState = p.onStateChange((s) => {
				isPlaying = s === 'playing';
			});

			// Poll currentTime/duration for the scrub bar. The provider APIs don't
			// expose a high-frequency tick event, so a 250ms poll is the standard
			// pattern (matches what react-player and similar use).
			pollInterval = setInterval(async () => {
				if (!player) return;
				try {
					currentTime = await player.getCurrentTime();
					if (!duration) duration = await player.getDuration();
				} catch { /* swallow — ad break or iframe detach */ }
			}, 250);
		})().catch((err) => console.warn('Embed player init failed:', err));

		return () => {
			cancelled = true;
			unsubState?.();
			if (pollInterval) clearInterval(pollInterval);
			player?.destroy();
			player = null;
			isPlaying = false;
			currentTime = 0;
			duration = 0;
		};
	});

	// Volume / muted / playback rate / startTime are reactive: when the user
	// tweaks them in the properties panel, push the change to the live player.
	$effect(() => {
		if (!player) return;
		try { player.setVolume(element.volume); } catch {}
	});
	$effect(() => {
		if (!player) return;
		try { player.setMuted(element.muted); isMuted = element.muted; } catch {}
	});
	$effect(() => {
		if (!player) return;
		try { player.setPlaybackRate(element.playbackRate); } catch {}
	});

	onDestroy(() => {
		unsubState?.();
		if (pollInterval) clearInterval(pollInterval);
		player?.destroy();
	});

	function togglePlay() {
		if (!player) return;
		if (isPlaying) player.pause();
		else player.play();
	}

	function toggleMute() {
		if (!player) return;
		isMuted = !isMuted;
		player.setMuted(isMuted);
	}

	function onScrub(e: Event) {
		const v = parseFloat((e.target as HTMLInputElement).value);
		if (!player || !isFinite(v)) return;
		player.seekTo(v);
		currentTime = v;
	}

	function fmtTime(s: number): string {
		if (!isFinite(s) || s < 0) s = 0;
		const m = Math.floor(s / 60);
		const r = Math.floor(s % 60);
		return `${m}:${String(r).padStart(2, '0')}`;
	}
</script>

{#if embed}
	<div class="embed-player">
		<iframe
			bind:this={iframeRef}
			class="embed-frame"
			class:passthrough={passthroughClicks}
			src={embed.embedSrc}
			title={embed.provider === 'youtube' ? 'YouTube video' : 'Vimeo video'}
			frameborder="0"
			allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
			referrerpolicy="strict-origin-when-cross-origin"
			allowfullscreen
		></iframe>

		{#if controlsOverlay}
			<div class="custom-controls" role="toolbar" aria-label="Video controls">
				<button class="ctrl-btn" onclick={togglePlay} aria-label={isPlaying ? 'Pause' : 'Play'}>
					{#if isPlaying}
						<svg viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="5" width="4" height="14"/><rect x="14" y="5" width="4" height="14"/></svg>
					{:else}
						<svg viewBox="0 0 24 24" fill="currentColor"><polygon points="6 4 20 12 6 20 6 4"/></svg>
					{/if}
				</button>
				<input
					class="scrub"
					type="range"
					min="0"
					max={duration || 1}
					step="0.1"
					value={currentTime}
					oninput={onScrub}
					aria-label="Seek"
				/>
				<span class="time">{fmtTime(currentTime)} / {fmtTime(duration)}</span>
				<button class="ctrl-btn" onclick={toggleMute} aria-label={isMuted ? 'Unmute' : 'Mute'}>
					{#if isMuted}
						<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><line x1="23" y1="9" x2="17" y2="15"/><line x1="17" y1="9" x2="23" y2="15"/></svg>
					{:else}
						<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>
					{/if}
				</button>
			</div>
		{/if}
	</div>
{/if}

<style>
	.embed-player {
		position: relative;
		width: 100%;
		height: 100%;
		background: #000;
		overflow: hidden;
	}
	.embed-frame {
		width: 100%;
		height: 100%;
		display: block;
		border: 0;
	}
	.embed-frame.passthrough {
		pointer-events: none;
	}
	.custom-controls {
		position: absolute;
		left: 8px;
		right: 8px;
		bottom: 8px;
		display: flex;
		align-items: center;
		gap: 10px;
		padding: 6px 10px;
		background: rgba(0, 0, 0, 0.6);
		backdrop-filter: blur(6px);
		border-radius: 8px;
		opacity: 0;
		transition: opacity 0.2s;
	}
	.embed-player:hover .custom-controls,
	.custom-controls:focus-within {
		opacity: 1;
	}
	.ctrl-btn {
		width: 28px;
		height: 28px;
		display: inline-flex;
		align-items: center;
		justify-content: center;
		background: transparent;
		border: 0;
		border-radius: 4px;
		color: #fff;
		cursor: pointer;
	}
	.ctrl-btn:hover { background: rgba(255, 255, 255, 0.15); }
	.ctrl-btn svg { width: 16px; height: 16px; }
	.scrub { flex: 1; accent-color: #6366f1; }
	.time {
		font-size: 11px;
		color: rgba(255, 255, 255, 0.85);
		font-variant-numeric: tabular-nums;
		min-width: 80px;
		text-align: right;
	}
</style>
