<script>
    import { onMount, onDestroy, createEventDispatcher } from 'svelte';
    import { browser } from '$app/environment';
    import { watchSync, isPlaying, canControl, isLeadIn } from '$lib/stores/watchSync';

    export let videoId = null;
    export let offlineImageUrl = null;

    const dispatch = createEventDispatcher();

    let player = null;
    let playerReady = false;
    let container;
    let syncCheckInterval = null;
    let lastSyncTime = 0;
    let lastSyncCheck = 0;
    let ignoreStateChange = false;
    let apiReady = false;
    let currentPlaylistItemId = null; // Track playlist item ID to detect changes even with same video
    let durationReportedForItemId = null; // Track which playlist item we've reported duration for
    let lastControllerSeekTime = 0; // Debounce controller seek updates

    const SYNC_CHECK_INTERVAL = 1000; // Check sync every second (matches server sync interval)
    const DRIFT_THRESHOLD = 2; // Seek if drift > 2 seconds (CyTube-style tight sync)
    const MIN_SYNC_INTERVAL = 1000; // Minimum 1 second between sync checks (server pushes every 1s)
    const CONTROLLER_SEEK_DEBOUNCE = 2000; // Debounce controller seeks by 2 seconds

    // Load YouTube IFrame API
    function loadYouTubeAPI() {
        if (!browser) return;

        if (window.YT && window.YT.Player) {
            apiReady = true;
            initPlayer();
            return;
        }

        // Check if script is already loading
        if (document.querySelector('script[src*="youtube.com/iframe_api"]')) {
            window.onYouTubeIframeAPIReady = () => {
                apiReady = true;
                initPlayer();
            };
            return;
        }

        const tag = document.createElement('script');
        tag.src = 'https://www.youtube.com/iframe_api';
        const firstScriptTag = document.getElementsByTagName('script')[0];
        if (firstScriptTag && firstScriptTag.parentNode) {
            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
        } else {
            document.head.appendChild(tag);
        }

        window.onYouTubeIframeAPIReady = () => {
            apiReady = true;
            initPlayer();
        };
    }

    // Try to initialize when container becomes available
    $: if (container && apiReady && !player) {
        initPlayer();
    }

    function initPlayer() {
        if (!container || player) return;
        if (!window.YT || !window.YT.Player) return;

        player = new window.YT.Player(container, {
            height: '100%',
            width: '100%',
            videoId: videoId || '',
            playerVars: {
                autoplay: 0,
                controls: 1,
                disablekb: 0,
                fs: 1,
                modestbranding: 1,
                rel: 0,
                playsinline: 1,
                origin: window.location.origin
            },
            events: {
                onReady: onPlayerReady,
                onStateChange: onPlayerStateChange,
                onError: onPlayerError
            }
        });
    }

    function onPlayerReady(event) {
        playerReady = true;
        dispatch('ready');

        // Start sync check interval
        if (syncCheckInterval) clearInterval(syncCheckInterval);
        syncCheckInterval = setInterval(checkAndSync, SYNC_CHECK_INTERVAL);
    }

    function onPlayerStateChange(event) {
        const state = event.data;

        // YT.PlayerState: UNSTARTED (-1), ENDED (0), PLAYING (1), PAUSED (2), BUFFERING (3), CUED (5)
        // Always handle ENDED state - crucial for playlist advancement
        if (state === window.YT.PlayerState.ENDED) {
            dispatch('ended');
            return;
        }

        // For other states, respect ignoreStateChange flag
        if (ignoreStateChange) return;

        if (state === window.YT.PlayerState.PLAYING) {
            dispatch('play', { time: player.getCurrentTime() });
            // If user clicked play and has control, notify server
            if ($canControl) {
                watchSync.play();
            }

            // Report duration when video starts playing (duration is now available)
            // Only report if we haven't already for this playlist item
            const storeState = $watchSync;
            const playlistItemId = storeState.currentVideo?.id;
            const storedDuration = storeState.currentVideo?.durationSeconds || 0;

            if (playlistItemId &&
                playlistItemId !== durationReportedForItemId &&
                storedDuration === 0) {
                const playerDuration = player.getDuration();
                if (playerDuration > 0) {
                    console.log(`Reporting duration for playlist item ${playlistItemId}: ${playerDuration}s`);
                    watchSync.reportDuration(playlistItemId, playerDuration);
                    durationReportedForItemId = playlistItemId;
                }
            }
        } else if (state === window.YT.PlayerState.PAUSED) {
            dispatch('pause', { time: player.getCurrentTime() });
            // If user clicked pause and has control, notify server
            if ($canControl) {
                watchSync.pause();
            }
        }
    }

    function onPlayerError(event) {
        console.error('YouTube player error:', event.data);
        dispatch('error', { code: event.data });
    }

    function checkAndSync(force = false) {
        if (!playerReady || !player) return;

        const storeState = $watchSync;
        if (!storeState.currentVideo) return;

        // Only sync if we have server time
        if (!storeState.serverTime) return;

        // Rate limit sync checks (unless forced)
        const now = Date.now();
        if (!force && (now - lastSyncCheck) < MIN_SYNC_INTERVAL) {
            return;
        }
        lastSyncCheck = now;

        const playerState = player.getPlayerState();
        const isPlayerPlaying = playerState === window.YT.PlayerState.PLAYING;
        const isBuffering = playerState === window.YT.PlayerState.BUFFERING;
        const shouldBePlaying = storeState.playbackState === 'playing';
        const hasVideoEnded = playerState === window.YT.PlayerState.ENDED;

        // During lead-in period, let video buffer without seeking
        // Server sends leadIn=true for 3 seconds after play starts
        if (storeState.leadIn) {
            // During lead-in, just ensure video is loading/buffering
            // Don't seek or sync position - wait for lead-in to complete
            if (shouldBePlaying && !isPlayerPlaying && !isBuffering && !hasVideoEnded) {
                ignoreStateChange = true;
                player.playVideo();
                setTimeout(() => { ignoreStateChange = false; }, 500);
            }
            return;
        }

        const expectedTime = watchSync.getExpectedTime();
        const currentTime = player.getCurrentTime();
        const drift = Math.abs(currentTime - expectedTime);

        // Check if we need to sync (tight 2-second threshold like CyTube)
        if (drift > DRIFT_THRESHOLD) {
            if ($canControl) {
                // Controller has seeked manually - update server with their position
                // Debounce to prevent spamming server while waiting for response
                const now = Date.now();
                if (now - lastControllerSeekTime > CONTROLLER_SEEK_DEBOUNCE) {
                    console.log(`Controller seek detected: drift=${drift.toFixed(2)}s, updating server to ${currentTime.toFixed(2)}s`);
                    watchSync.seek(currentTime);
                    lastControllerSeekTime = now;
                }
            } else {
                // Non-controller - sync back to server time
                console.log(`Sync drift detected: ${drift.toFixed(2)}s, seeking to ${expectedTime.toFixed(2)}s`);
                ignoreStateChange = true;
                player.seekTo(expectedTime, true);
                setTimeout(() => { ignoreStateChange = false; }, 500);
            }
        }

        // Sync play/pause state
        // Don't try to restart a video that has naturally ended - wait for skip/next
        if (shouldBePlaying && !isPlayerPlaying && !isBuffering && !hasVideoEnded) {
            ignoreStateChange = true;
            player.playVideo();
            setTimeout(() => { ignoreStateChange = false; }, 500);
        } else if (!shouldBePlaying && isPlayerPlaying) {
            ignoreStateChange = true;
            player.pauseVideo();
            setTimeout(() => { ignoreStateChange = false; }, 500);
        }
    }

    // React to video changes from the store - track by playlist item ID, not just YouTube video ID
    // This handles the case where the same YouTube video appears multiple times in the playlist
    $: if (playerReady && $watchSync.currentVideo?.id !== currentPlaylistItemId) {
        currentPlaylistItemId = $watchSync.currentVideo?.id;
        durationReportedForItemId = null; // Reset so we report duration for new video
        const newVideoId = $watchSync.currentVideo?.youtubeVideoId;
        if (newVideoId && player) {
            videoId = newVideoId;
            ignoreStateChange = true;
            // Reset sync timing when video changes to avoid immediate re-sync
            lastSyncCheck = Date.now();
            lastSyncTime = 0;
            player.loadVideoById({
                videoId: newVideoId,
                startSeconds: $watchSync.currentTime || 0
            });
            setTimeout(() => { ignoreStateChange = false; }, 1000);
        }
    }

    // React to state changes from the store
    $: if (playerReady && player && $watchSync.serverTime > lastSyncTime) {
        lastSyncTime = $watchSync.serverTime;
        checkAndSync();
    }

    // Handle window event for state changes from other users
    function handleStateChange(event) {
        if (!playerReady || !player) return;

        const { event: action, currentTime, triggeredBy } = event.detail;

        ignoreStateChange = true;

        if (action === 'play') {
            player.playVideo();
            // Force sync after state change from another user
            setTimeout(() => checkAndSync(true), 1000);
        } else if (action === 'pause') {
            player.pauseVideo();
        } else if (action === 'seek' && currentTime !== undefined) {
            player.seekTo(currentTime, true);
        } else if (action === 'skip' || action === 'video_changed') {
            // Video change will be handled by the reactive statement above
            setTimeout(() => checkAndSync(true), 1000);
        }

        setTimeout(() => { ignoreStateChange = false; }, 1000);
    }

    // Public methods for external control
    export function seekTo(time) {
        if (playerReady && player) {
            ignoreStateChange = true;
            player.seekTo(time, true);
            setTimeout(() => { ignoreStateChange = false; }, 500);
        }
    }

    export function getCurrentTime() {
        return playerReady && player ? player.getCurrentTime() : 0;
    }

    export function getDuration() {
        return playerReady && player ? player.getDuration() : 0;
    }

    onMount(() => {
        loadYouTubeAPI();

        if (browser) {
            window.addEventListener('watch-state-change', handleStateChange);
        }
    });

    onDestroy(() => {
        if (syncCheckInterval) {
            clearInterval(syncCheckInterval);
        }

        if (browser) {
            window.removeEventListener('watch-state-change', handleStateChange);
        }

        if (player) {
            player.destroy();
            player = null;
        }
    });
</script>

<style>
    .youtube-player-container {
        width: 100%;
        aspect-ratio: 16 / 9;
        background: #000;
        border-radius: 8px;
        overflow: hidden;
        position: relative;
    }

    .youtube-player-container :global(iframe) {
        width: 100%;
        height: 100%;
        border: none;
    }

    .no-video {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        height: 100%;
        color: var(--gray);
        gap: 1rem;
    }

    .no-video svg {
        width: 64px;
        height: 64px;
        opacity: 0.5;
    }

    .offline-image {
        width: 100%;
        height: 100%;
        object-fit: cover;
    }
</style>

<div class="youtube-player-container">
    {#if videoId || $watchSync.currentVideo}
        <div bind:this={container}></div>
    {:else if offlineImageUrl}
        <img src={offlineImageUrl} alt="No video playing" class="offline-image" />
    {:else}
        <div class="no-video">
            <svg viewBox="0 0 24 24" fill="currentColor">
                <path d="M19.615 3.184c-3.604-.246-11.631-.245-15.23 0-3.897.266-4.356 2.62-4.385 8.816.029 6.185.484 8.549 4.385 8.816 3.6.245 11.626.246 15.23 0 3.897-.266 4.356-2.62 4.385-8.816-.029-6.185-.484-8.549-4.385-8.816zm-10.615 12.816v-8l8 3.993-8 4.007z"/>
            </svg>
            <span>No video playing</span>
            <span style="font-size: 0.85rem;">Add a video to the playlist to start watching</span>
        </div>
    {/if}
</div>
