document.addEventListener('DOMContentLoaded', () => {
    const videoListEl = document.getElementById('video-list');
    const loadingSpinner = document.getElementById('loading-spinner');
    const refreshBtn = document.getElementById('refresh-btn');
    const clearDownloadCacheBtn = document.getElementById('clear-download-cache-btn');
    const bucketSelect = document.getElementById('bucket-select');
    const loginScreen = document.getElementById('login-screen');
    const appContainer = document.getElementById('app-container');
    const loginUsernameInput = document.getElementById('login-username');
    const loginPasswordInput = document.getElementById('login-password');
    const loginBtn = document.getElementById('login-btn');
    const loginError = document.getElementById('login-error');
    const encoderSelect = document.getElementById('encoder-select');
    const playerOverlay = document.getElementById('player-overlay');
    const transcodingOverlay = document.getElementById('transcoding-overlay');
    const videoPlayer = document.getElementById('video-player');
    const playerWrapper = document.getElementById('player-wrapper');
    const nowPlaying = document.getElementById('now-playing');
    const currentVideoTitle = document.getElementById('current-video-title');
    const transcodeBtn = document.getElementById('transcode-btn');
    const stopTranscodeBtn = document.getElementById('stop-transcode-btn');
    const themeSelector = document.getElementById('theme-selector');
    const currentUsername = document.getElementById('current-username');
    const logoutBtn = document.getElementById('logout-btn');
    const topBannerTitle = document.getElementById('top-banner-title');
    const playBtn = document.getElementById('play-btn');
    const topBanner = document.getElementById('top-banner');
    const customControls = document.getElementById('custom-controls');
    const controlPlayToggle = document.getElementById('control-play-toggle');
    const controlMuteToggle = document.getElementById('control-mute-toggle');
    const controlFullscreenToggle = document.getElementById('control-fullscreen-toggle');
    const controlSpeedToggle = document.getElementById('control-speed-toggle');
    const volumeSlider = document.getElementById('volume-slider');
    const volumeValue = document.getElementById('volume-value');
    const playbackStatus = document.getElementById('playback-status');
    const playbackStatusText = document.getElementById('playback-status-text');
    const playbackSpeed = document.getElementById('playback-speed');

    // Download phase elements
    const downloadPhase = document.getElementById('download-phase');
    const downloadSizeText = document.getElementById('download-size-text');
    const downloadProgressText = document.getElementById('download-progress-text');
    const downloadProgressFill = document.getElementById('download-progress-fill');

    // Transcode phase elements
    const transcodePhase = document.getElementById('transcode-phase');
    const transcodeDetailText = document.getElementById('transcode-detail-text');
    const transcodeProgressText = document.getElementById('transcode-progress-text');
    const transcodeProgressFill = document.getElementById('transcode-progress-fill');
    const transcodeStats = document.getElementById('transcode-stats');
    const statFps = document.getElementById('stat-fps');
    const statBitrate = document.getElementById('stat-bitrate');
    const statTime = document.getElementById('stat-time');

    // Custom seek bar elements
    const customSeekContainer = document.getElementById('custom-seek-container');
    const seekBar = document.getElementById('seek-bar');
    const seekBarTranscode = document.getElementById('seek-bar-transcode');
    const seekBarProgress = document.getElementById('seek-bar-progress');
    const seekBarHandle = document.getElementById('seek-bar-handle');
    const seekCurrentTime = document.getElementById('seek-current-time');
    const seekTotalTime = document.getElementById('seek-total-time');
    const seekingOverlay = document.getElementById('seeking-overlay');

    let hlsInstance = null;
    let currentPollInterval = null;
    let selectedBucket = null;
    let selectedKey = null;
    let ws = null;
    let wsConnected = false;
    let subscribedKey = null;
    let currentVideoKey = null;
    let s3Username = '';
    let s3Password = '';
    let s3AuthHeaders = {};

    // Seek state
    let videoDuration = 0;
    let seekOffset = 0;
    let isDraggingSeek = false;
    let isStreamActive = false;
    let pendingSeekTimeout = null;
    let internalSeeking = false;
    let lastAbsolutePlaybackTime = 0;
    let lastVolumeBeforeMute = 1;
    let controlsHideTimeout = null;
    let controlsHovered = false;
    let controlsPointerReleaseTimeout = null;

    if (videoPlayer) {
        videoPlayer.controls = true;
    }
    if (playbackSpeed) {
        videoPlayer.playbackRate = parseFloat(playbackSpeed.value) || 1;
    }


    const formatBytes = (bytes) => {
        if (!bytes || bytes === 0) return '0 B';
        const units = ['B', 'KB', 'MB', 'GB', 'TB'];
        const k = 1024;
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return (bytes / Math.pow(k, i)).toFixed(i > 0 ? 2 : 0) + ' ' + units[i];
    };

    const formatTime = (seconds) => {
        if (!seconds || !isFinite(seconds) || seconds < 0) return '00:00:00';
        const h = Math.floor(seconds / 3600);
        const m = Math.floor((seconds % 3600) / 60);
        const s = Math.floor(seconds % 60);
        return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
    };

    const sendWsMessage = (message) => {
        if (ws && ws.readyState === WebSocket.OPEN) {
            ws.send(JSON.stringify(message));
        }
    };

    const createStreamSessionId = () => `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;

    const populateSelect = (selectEl, options, fallbackValue) => {
        if (!selectEl) return;
        selectEl.innerHTML = '';
        (options || []).forEach((item) => {
            const option = document.createElement('option');
            option.value = item.value;
            option.textContent = item.label || item.value;
            selectEl.appendChild(option);
        });

        const preferredValue = options?.some((item) => item.value === fallbackValue)
            ? fallbackValue
            : options?.[0]?.value;

        if (preferredValue) {
            selectEl.value = preferredValue;
        }
    };

    const buildHlsPlaylistUrl = () => {
        const decoder = 'auto';
        const encoder = encoderSelect?.value || 'h264_rkmpp';
        let streamUrl = `/api/hls/playlist.m3u8?bucket=${encodeURIComponent(selectedBucket)}&key=${encodeURIComponent(selectedKey)}&decoder=${encodeURIComponent(decoder)}&encoder=${encodeURIComponent(encoder)}`;
        const sessionId = localStorage.getItem('sessionId');
        if (sessionId) {
            streamUrl += `&sessionId=${encodeURIComponent(sessionId)}`;
        } else {
            if (s3Username) streamUrl += `&username=${encodeURIComponent(s3Username)}`;
            if (s3Password) streamUrl += `&password=${encodeURIComponent(s3Password)}`;
        }
        return streamUrl;
    };

    const updatePlayControls = () => {
        if (controlPlayToggle) {
            controlPlayToggle.textContent = videoPlayer.paused ? '>' : '||';
            controlPlayToggle.setAttribute('aria-label', videoPlayer.paused ? 'Play' : 'Pause');
        }
        if (playbackStatus) {
            playbackStatus.classList.remove('playing', 'paused', 'seeking');
            playbackStatus.classList.add(videoPlayer.paused ? 'paused' : 'playing');
        }
        if (playbackStatusText) {
            playbackStatusText.textContent = videoPlayer.paused ? 'Paused' : 'Playing';
        }
        if (playBtn) {
            if (videoPlayer.paused && isStreamActive) {
                playBtn.disabled = false;
                playBtn.textContent = 'Play';
                playBtn.classList.remove('hidden');
            } else {
                playBtn.classList.add('hidden');
            }
        }
    };

    const updateVolumeControls = () => {
        const effectiveVolume = videoPlayer.muted ? 0 : videoPlayer.volume;
        if (volumeSlider) {
            volumeSlider.value = String(effectiveVolume);
        }
        if (volumeValue) {
            volumeValue.textContent = `${Math.round(effectiveVolume * 100)}%`;
        }
        if (controlMuteToggle) {
            controlMuteToggle.textContent = effectiveVolume === 0 ? 'x' : 'o';
            controlMuteToggle.setAttribute('aria-label', effectiveVolume === 0 ? 'Unmute' : 'Mute');
        }
    };

    const updateFullscreenControls = () => {
        if (controlFullscreenToggle) {
            controlFullscreenToggle.textContent = '[]';
            controlFullscreenToggle.setAttribute('aria-label', document.fullscreenElement ? 'Exit Fullscreen' : 'Fullscreen');
        }
    };

    const updateSpeedControls = () => {
        if (controlSpeedToggle && playbackSpeed) {
            controlSpeedToggle.textContent = playbackSpeed.value === '1' ? '1x' : playbackSpeed.value;
        }
    };

    const updateTranscodeProgressBar = (percent = 0) => {
        if (!seekBarTranscode) return;
        const safePercent = Math.min(Math.max(Math.round(percent || 0), 0), 100);
        seekBarTranscode.style.width = `${safePercent}%`;
    };

    const showCustomControls = () => {
        if (customControls) {
            customControls.classList.remove('hidden');
            customControls.classList.remove('controls-faded');
        }
        if (customSeekContainer) {
            customSeekContainer.classList.remove('controls-faded');
        }
    };

    const hideCustomControls = () => {
        if (customControls) {
            customControls.classList.add('hidden');
        }
        if (customSeekContainer) {
            customSeekContainer.classList.remove('controls-faded');
        }
    };

    const revealPlaybackChrome = () => {
        if (!isStreamActive) return;
        if (customControls) customControls.classList.remove('controls-faded');
        if (customSeekContainer) customSeekContainer.classList.remove('controls-faded');
    };

    const fadePlaybackChrome = () => {
        if (!isStreamActive || videoPlayer.paused || controlsHovered || isDraggingSeek) return;
        if (customControls) customControls.classList.add('controls-faded');
        if (customSeekContainer) customSeekContainer.classList.add('controls-faded');
    };

    const schedulePlaybackChromeHide = () => {
        if (controlsHideTimeout) {
            clearTimeout(controlsHideTimeout);
        }
        revealPlaybackChrome();
        if (!isStreamActive || videoPlayer.paused || controlsHovered) {
            return;
        }
        controlsHideTimeout = setTimeout(() => {
            fadePlaybackChrome();
            controlsHideTimeout = null;
        }, 1800);
    };

    const setPlaybackStatus = (statusText, statusClass) => {
        if (playbackStatus) {
            playbackStatus.classList.remove('playing', 'paused', 'seeking');
            if (statusClass) playbackStatus.classList.add(statusClass);
        }
        if (playbackStatusText) {
            playbackStatusText.textContent = statusText;
        }
    };

    const subscribeToKey = (key) => {
        subscribedKey = key;
        if (wsConnected) {
            sendWsMessage({ type: 'subscribe', key });
        }
    };

    const handleWsMessage = (event) => {
        try {
            const message = JSON.parse(event.data);
            if (message.key !== currentVideoKey) return;

            if (message.type === 'duration' && message.duration) {
                videoDuration = message.duration;
                if (seekTotalTime) seekTotalTime.textContent = formatTime(videoDuration);
                showSeekBar();
                updateSeekBarPosition(seekOffset + (videoPlayer.currentTime || 0));
            }
            if (message.type === 'progress') {
                handleProgress(message.progress);
            }
            if (message.type === 'ready') {
                handleProgress({ status: 'finished', percent: 100, details: 'Ready to play' });
                playMp4Stream(message.mp4Url);
            }
        } catch (error) {
            console.error('WebSocket message parse error:', error);
        }
    };

    const handleProgress = (progress) => {
        if (!progress) return;
        const status = progress.status;

        if (status === 'downloading') {
            showDownloadPhase();
            const percent = Math.min(Math.max(Math.round(progress.percent || 0), 0), 100);
            updateTranscodeProgressBar(percent);
            const downloaded = formatBytes(progress.downloadedBytes || 0);
            const total = formatBytes(progress.totalBytes || 0);
            downloadSizeText.textContent = `${downloaded} / ${total}`;
            downloadProgressText.textContent = `${percent}%`;
            downloadProgressFill.style.width = `${percent}%`;
        } else if (status === 'downloaded') {
            showDownloadPhase();
            updateTranscodeProgressBar(100);
            const downloaded = formatBytes(progress.downloadedBytes || progress.totalBytes || 0);
            const total = formatBytes(progress.totalBytes || 0);
            downloadSizeText.textContent = `${downloaded} / ${total} — 下载完成`;
            downloadProgressText.textContent = '100%';
            downloadProgressFill.style.width = '100%';
            setTimeout(() => {
                showTranscodePhase();
            }, 600);
        } else if (status === 'transcoding') {
            showTranscodePhase();
            const percent = Math.min(Math.max(Math.round(progress.percent || 0), 0), 100);
            updateTranscodeProgressBar(percent);
            transcodeProgressText.textContent = `${percent}%`;
            transcodeProgressFill.style.width = `${percent}%`;
            transcodeDetailText.textContent = progress.details || 'FFmpeg 转码中...';

            if (progress.fps || progress.bitrate || progress.timemark) {
                transcodeStats.classList.remove('hidden');
                statFps.textContent = progress.fps ? `${progress.fps} fps` : '';
                statBitrate.textContent = progress.bitrate ? `${progress.bitrate} kbps` : '';
                statTime.textContent = progress.timemark ? `${progress.timemark}` : '';
            }
        } else if (status === 'finished') {
            showTranscodePhase();
            updateTranscodeProgressBar(100);
            transcodeProgressText.textContent = '100%';
            transcodeProgressFill.style.width = '100%';
            transcodeDetailText.textContent = '转码完成';
        } else if (status === 'failed') {
            updateTranscodeProgressBar(progress.percent || 0);
            transcodeDetailText.textContent = `失败: ${progress.details || '未知错误'}`;
            transcodeProgressFill.style.background = 'linear-gradient(90deg, #dc2626, #b91c1c)';
        } else if (status === 'cancelled') {
            updateTranscodeProgressBar(0);
            transcodeDetailText.textContent = '已取消';
            transcodeProgressFill.style.width = '0%';
        }
    };

    const showDownloadPhase = () => {
        if (downloadPhase) downloadPhase.classList.remove('hidden');
        if (transcodePhase) transcodePhase.classList.add('hidden');
    };

    const showTranscodePhase = () => {
        if (downloadPhase) downloadPhase.classList.add('hidden');
        if (transcodePhase) transcodePhase.classList.remove('hidden');
    };

    const resetPhases = () => {
        if (downloadPhase) downloadPhase.classList.remove('hidden');
        if (downloadSizeText) downloadSizeText.textContent = '准备下载...';
        if (downloadProgressText) downloadProgressText.textContent = '0%';
        if (downloadProgressFill) downloadProgressFill.style.width = '0%';

        if (transcodePhase) transcodePhase.classList.add('hidden');
        if (transcodeDetailText) transcodeDetailText.textContent = 'FFmpeg 转码中...';
        if (transcodeProgressText) transcodeProgressText.textContent = '0%';
        if (transcodeProgressFill) {
            transcodeProgressFill.style.width = '0%';
            transcodeProgressFill.style.background = '';
        }
        if (transcodeStats) transcodeStats.classList.add('hidden');
        if (statFps) statFps.textContent = '';
        if (statBitrate) statBitrate.textContent = '';
        if (statTime) statTime.textContent = '';
        updateTranscodeProgressBar(0);

        if (stopTranscodeBtn) {
            stopTranscodeBtn.classList.add('hidden');
            stopTranscodeBtn.disabled = false;
            stopTranscodeBtn.textContent = 'Stop Transcode';
        }
    };

    // --- Custom seek bar ---
    const showSeekBar = () => {
        if (customSeekContainer && videoDuration > 0) {
            customSeekContainer.classList.remove('hidden');
        }
    };

    const hideSeekBar = () => {
        if (customSeekContainer) customSeekContainer.classList.add('hidden');
    };

    const updateSeekBarPosition = (absoluteTime) => {
        if (!seekBar || videoDuration <= 0) return;
        const ratio = Math.max(0, Math.min(1, absoluteTime / videoDuration));
        seekBarProgress.style.width = `${ratio * 100}%`;
        seekBarHandle.style.left = `${ratio * 100}%`;
        seekCurrentTime.textContent = formatTime(absoluteTime);
    };

    // Track playback position in the custom seek bar
    videoPlayer.addEventListener('timeupdate', () => {
        if (isDraggingSeek || !isStreamActive) return;
        const absoluteTime = seekOffset + videoPlayer.currentTime;
        lastAbsolutePlaybackTime = absoluteTime;
        updateSeekBarPosition(absoluteTime);
    });

    videoPlayer.addEventListener('play', () => {
        updatePlayControls();
        schedulePlaybackChromeHide();
    });
    videoPlayer.addEventListener('pause', () => {
        updatePlayControls();
        revealPlaybackChrome();
    });
    videoPlayer.addEventListener('ended', () => {
        updatePlayControls();
        revealPlaybackChrome();
    });
    videoPlayer.addEventListener('volumechange', updateVolumeControls);





    // Seek bar interaction: click or drag
    const getSeekRatio = (e) => {
        const rect = seekBar.getBoundingClientRect();
        return Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
    };

    const handleSeekStart = (e) => {
        if (!isStreamActive || videoDuration <= 0) return;
        e.preventDefault();
        isDraggingSeek = true;
        seekBar.classList.add('seeking');
        const ratio = getSeekRatio(e);
        const targetTime = ratio * videoDuration;
        updateSeekBarPosition(targetTime);
    };

    const handleSeekMove = (e) => {
        if (!isDraggingSeek) return;
        e.preventDefault();
        const ratio = getSeekRatio(e);
        const targetTime = ratio * videoDuration;
        updateSeekBarPosition(targetTime);
    };

    const handleSeekEnd = (e) => {
        if (!isDraggingSeek) return;
        isDraggingSeek = false;
        seekBar.classList.remove('seeking');
        const ratio = getSeekRatio(e);
        const targetTime = ratio * videoDuration;
        seekToTime(targetTime);
    };

    if (seekBar) {
        seekBar.addEventListener('mousedown', handleSeekStart);
        document.addEventListener('mousemove', handleSeekMove);
        document.addEventListener('mouseup', handleSeekEnd);

        // Touch support
        seekBar.addEventListener('touchstart', (e) => {
            const touch = e.touches[0];
            handleSeekStart({ clientX: touch.clientX, preventDefault: () => e.preventDefault() });
        });
        document.addEventListener('touchmove', (e) => {
            if (!isDraggingSeek) return;
            const touch = e.touches[0];
            handleSeekMove({ clientX: touch.clientX, preventDefault: () => e.preventDefault() });
        });
        document.addEventListener('touchend', (e) => {
            if (!isDraggingSeek) return;
            const touch = e.changedTouches[0];
            handleSeekEnd({ clientX: touch.clientX });
        });
    }

    if (playerWrapper) {
        playerWrapper.addEventListener('mousemove', () => {
            controlsHovered = true;
            schedulePlaybackChromeHide();
            if (controlsPointerReleaseTimeout) {
                clearTimeout(controlsPointerReleaseTimeout);
            }
            controlsPointerReleaseTimeout = setTimeout(() => {
                controlsHovered = false;
                schedulePlaybackChromeHide();
                controlsPointerReleaseTimeout = null;
            }, 1200);
        });
        playerWrapper.addEventListener('mouseenter', () => {
            controlsHovered = true;
            revealPlaybackChrome();
        });
        playerWrapper.addEventListener('mouseleave', () => {
            controlsHovered = false;
            schedulePlaybackChromeHide();
        });
    }

    if (customControls) {
        customControls.addEventListener('mouseenter', () => {
            controlsHovered = true;
            revealPlaybackChrome();
        });
        customControls.addEventListener('mouseleave', () => {
            controlsHovered = false;
            schedulePlaybackChromeHide();
        });
    }

    if (customSeekContainer) {
        customSeekContainer.addEventListener('mouseenter', () => {
            controlsHovered = true;
            revealPlaybackChrome();
        });
        customSeekContainer.addEventListener('mouseleave', () => {
            controlsHovered = false;
            schedulePlaybackChromeHide();
        });
    }



    // --- End custom seek bar ---

    const loadConfig = async () => {
        if (!topBanner) return;
        try {
            const res = await fetch('/api/config');
            if (!res.ok) throw new Error('Failed to load config');
            const data = await res.json();
            const title = data.title || 'S3 Media Transcoder';
            if (topBannerTitle) topBannerTitle.textContent = title;
            topBanner.classList.remove('hidden');
            document.title = title;
            populateSelect(encoderSelect, data.videoEncoders || [], data.defaultVideoEncoder || 'h264_rkmpp');
        } catch (err) {
            console.error('Config load failed:', err);
        }
    };

    const connectWebSocket = () => {
        const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
        ws = new WebSocket(`${protocol}://${window.location.host}`);

        ws.addEventListener('open', () => {
            wsConnected = true;
            if (subscribedKey) {
                sendWsMessage({ type: 'subscribe', key: subscribedKey });
            }
        });

        ws.addEventListener('message', handleWsMessage);

        ws.addEventListener('close', () => {
            wsConnected = false;
            setTimeout(connectWebSocket, 2000);
        });

        ws.addEventListener('error', (error) => {
            console.error('WebSocket error:', error);
            wsConnected = false;
        });
    };

    const setAuthHeaders = (username, password) => {
        s3Username = typeof username === 'string' ? username : '';
        s3Password = typeof password === 'string' ? password : '';
        s3AuthHeaders = {};
        if (s3Username) s3AuthHeaders['X-S3-Username'] = s3Username;
        if (s3Password) s3AuthHeaders['X-S3-Password'] = s3Password;
    };

    const setSessionAuth = (sessionId, username) => {
        s3AuthHeaders = { 'X-Session-ID': sessionId };
        localStorage.setItem('sessionId', sessionId);
        if (username) {
            localStorage.setItem('username', username);
            if (currentUsername) currentUsername.textContent = username;
        }
    };

    const clearSessionAuth = () => {
        s3AuthHeaders = {};
        localStorage.removeItem('sessionId');
        localStorage.removeItem('username');
    };

    const showLogin = () => {
        if (loginScreen) loginScreen.classList.remove('hidden');
        if (appContainer) appContainer.classList.add('hidden');
    };

    const showApp = () => {
        if (loginScreen) loginScreen.classList.add('hidden');
        if (appContainer) appContainer.classList.remove('hidden');
    };

    const renderBuckets = (buckets) => {
        if (!bucketSelect) return;
        bucketSelect.innerHTML = '<option value="" disabled selected>Choose a bucket</option>';
        buckets.forEach(bucket => {
            const option = document.createElement('option');
            option.value = bucket.Name;
            option.textContent = bucket.Name;
            bucketSelect.appendChild(option);
        });
    };

    const showLoginError = (message) => {
        if (!loginError) return;
        loginError.textContent = message;
        loginError.classList.remove('hidden');
    };

    const clearLoginError = () => {
        if (!loginError) return;
        loginError.textContent = '';
        loginError.classList.add('hidden');
    };

    const login = async () => {
        if (!loginUsernameInput || !loginPasswordInput) return;
        const username = loginUsernameInput.value.trim();
        const password = loginPasswordInput.value;
        if (!username || !password) {
            showLoginError('Please enter both access key and secret key.');
            return;
        }
        loginBtn.disabled = true;
        loginBtn.textContent = 'Logging in...';
        clearLoginError();

        try {
            const res = await fetch('/api/login', { 
                method: 'POST', 
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ username, password })
            });
            const data = await res.json();
            if (!res.ok) {
                throw new Error(data.error || 'Login failed');
            }
            if (!Array.isArray(data.buckets) || data.buckets.length === 0) {
                throw new Error('No buckets available for this account');
            }
            setSessionAuth(data.sessionId, data.username);
            renderBuckets(data.buckets);
            showApp();
            selectedBucket = data.buckets[0].Name;
            if (bucketSelect) bucketSelect.value = selectedBucket;
            loadConfig();
            connectWebSocket();
            await fetchVideos(selectedBucket);
        } catch (err) {
            console.error('Login error:', err);
            showLoginError(err.message);
            clearSessionAuth();
        } finally {
            if (loginBtn) {
                loginBtn.disabled = false;
                loginBtn.textContent = 'Login';
            }
        }
    };

    const clearDownloadCache = async () => {
        if (!clearDownloadCacheBtn) return;
        clearDownloadCacheBtn.disabled = true;
        clearDownloadCacheBtn.textContent = '清空中...';

        try {
            const res = await fetch('/api/clear-download-cache', { method: 'POST' });
            if (!res.ok) {
                const data = await res.json().catch(() => ({}));
                throw new Error(data.error || '清空下载缓存失败');
            }
            alert('下载缓存已清空');
        } catch (err) {
            console.error('Clear download cache failed:', err);
            alert(`清空下载缓存失败: ${err.message}`);
        } finally {
            clearDownloadCacheBtn.disabled = false;
            clearDownloadCacheBtn.textContent = '清空下载缓存';
        }
    };

    if (transcodeBtn) {
        transcodeBtn.addEventListener('click', () => {
            startTranscode();
        });
    }

    if (playBtn) {
        playBtn.addEventListener('click', () => {
            videoPlayer.play().catch(e => console.log('Play blocked:', e));
            playBtn.classList.add('hidden');
        });
    }

    // Fetch list of videos from the backend
    const fetchVideos = async (bucket) => {
        if (!bucket) {
            return;
        }
        videoListEl.classList.add('hidden');
        loadingSpinner.classList.remove('hidden');
        videoListEl.innerHTML = '';

        try {
            const res = await fetch(`/api/videos?bucket=${encodeURIComponent(bucket)}`, { headers: s3AuthHeaders });
            if (!res.ok) throw new Error('Failed to fetch videos. Check S3 Config.');

            const data = await res.json();

            loadingSpinner.classList.add('hidden');
            videoListEl.classList.remove('hidden');

            if (data.videos.length === 0) {
                videoListEl.innerHTML = '<p style="color: var(--text-secondary); text-align: center; padding: 2rem;">No videos found.</p>';
                return;
            }

            // Build a tree structure from S3 keys
            const tree = {};
            data.videos.forEach(key => {
                const parts = key.split('/');
                let current = tree;
                parts.forEach((part, index) => {
                    if (!current[part]) {
                        current[part] = {};
                    }
                    if (index === parts.length - 1) {
                        current[part].__file = key;
                    }
                    current = current[part];
                });
            });

            const createFileItem = (name, key) => {
                const li = document.createElement('li');
                li.className = 'video-item file-item';
                const ext = name.split('.').pop().toUpperCase();
                li.innerHTML = `
                    <div class="video-icon">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m22 8-6 4 6 4V8Z"/><rect width="14" height="12" x="2" y="6" rx="2" ry="2"/></svg>
                    </div>
                    <div class="video-info">
                        <div class="video-title" title="${key}">${name}</div>
                        <div class="video-meta">Video / ${ext}</div>
                    </div>
                `;
                li.addEventListener('click', (e) => {
                    e.stopPropagation();
                    selectVideo(key, li);
                });
                return li;
            };

            const renderTree = (node, container) => {
                for (const [name, value] of Object.entries(node)) {
                    if (name === '__file') continue;

                    const childKeys = Object.keys(value).filter(key => key !== '__file');
                    const hasChildren = childKeys.length > 0;
                    const isFile = typeof value.__file === 'string';

                    if (hasChildren) {
                        const li = document.createElement('li');
                        li.className = 'folder-item';

                        const folderHeader = document.createElement('div');
                        folderHeader.className = 'folder-header';
                        folderHeader.innerHTML = `
                            <div class="folder-icon">
                                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
                            </div>
                            <div class="folder-title" title="${name}">${name}</div>
                            <div class="folder-toggle">
                                <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="chevron"><path d="m9 18 6-6-6-6"/></svg>
                            </div>
                        `;

                        const subListContainer = document.createElement('ul');
                        subListContainer.className = 'sub-list hidden';

                        folderHeader.addEventListener('click', (e) => {
                            e.stopPropagation();
                            li.classList.toggle('open');
                            subListContainer.classList.toggle('hidden');
                        });

                        li.appendChild(folderHeader);
                        if (isFile) {
                            subListContainer.appendChild(createFileItem(name, value.__file));
                        }
                        renderTree(value, subListContainer);
                        li.appendChild(subListContainer);
                        container.appendChild(li);
                    } else if (isFile) {
                        container.appendChild(createFileItem(name, value.__file));
                    }
                }
            };

            renderTree(tree, videoListEl);
        } catch (err) {
            console.error(err);
            loadingSpinner.innerHTML = `<p style="color: #ef4444;">Error: ${err.message}</p>`;
        }
    };

    // Handle video selection
    const selectVideo = (key, listItemNode) => {
        document.querySelectorAll('.video-item').forEach(n => n.classList.remove('active'));
        listItemNode.classList.add('active');

        stopPolling();
        playerOverlay.classList.add('hidden');
        videoPlayer.classList.add('hidden');
        videoPlayer.pause();
        videoPlayer.removeAttribute('src');
        if (hlsInstance) {
            hlsInstance.destroy();
            hlsInstance = null;
        }
        videoPlayer.load();
        isStreamActive = false;
        videoDuration = 0;
        seekOffset = 0;
        if (seekCurrentTime) seekCurrentTime.textContent = formatTime(0);
        if (seekTotalTime) seekTotalTime.textContent = formatTime(0);
        hideSeekBar();
        hideCustomControls();
        setPlaybackStatus('Paused', 'paused');
        updatePlayControls();

        if (transcodeBtn) {
            transcodeBtn.disabled = false;
            transcodeBtn.textContent = 'Start Transcode';
            transcodeBtn.classList.remove('hidden');
        }
        if (stopTranscodeBtn) {
            stopTranscodeBtn.classList.add('hidden');
            stopTranscodeBtn.disabled = false;
            stopTranscodeBtn.textContent = 'Stop Transcode';
        }
        if (playBtn) {
            playBtn.classList.add('hidden');
        }
        resetPhases();

        selectedKey = key;
        currentVideoKey = key;
        subscribeToKey(key);

        nowPlaying.classList.remove('hidden');
        currentVideoTitle.textContent = key.split('/').pop();
    };

    const startTranscode = async () => {
        if (!selectedKey) return;
        if (transcodeBtn) {
            transcodeBtn.disabled = true;
            transcodeBtn.textContent = 'Starting...';
        }
        if (stopTranscodeBtn) {
            stopTranscodeBtn.classList.remove('hidden');
            stopTranscodeBtn.disabled = false;
            stopTranscodeBtn.textContent = 'Stop Transcode';
        }
        stopPolling();
        resetPhases();
        seekOffset = 0;
        videoDuration = 0;
        isStreamActive = false;
        hideSeekBar();
        hideCustomControls();
        setPlaybackStatus('Paused', 'paused');
        transcodingOverlay.classList.remove('hidden');

        try {
            if (!selectedBucket) throw new Error('No bucket selected');
            const streamUrl = buildHlsPlaylistUrl();
            if (hlsInstance) {
                hlsInstance.destroy();
                hlsInstance = null;
            }
            if (window.Hls && Hls.isSupported()) {
                hlsInstance = new Hls({ maxBufferLength: 30, maxMaxBufferLength: 60 });
                hlsInstance.loadSource(streamUrl);
                hlsInstance.attachMedia(videoPlayer);
                hlsInstance.on(Hls.Events.MANIFEST_PARSED, () => {
                    transcodingOverlay.classList.add('hidden');
                    videoPlayer.classList.remove('hidden');
                    isStreamActive = true;
                    videoPlayer.play().catch(() => { });
                    showSeekBar();
                    showCustomControls();
                    updatePlayControls();
                    updateVolumeControls();
                    updateFullscreenControls();
                    schedulePlaybackChromeHide();
                });
                hlsInstance.on(Hls.Events.ERROR, function (event, data) {
                    if (data.fatal) {
                        switch (data.type) {
                            case Hls.ErrorTypes.NETWORK_ERROR:
                                hlsInstance.startLoad();
                                break;
                            case Hls.ErrorTypes.MEDIA_ERROR:
                                hlsInstance.recoverMediaError();
                                break;
                            default:
                                hlsInstance.destroy();
                                break;
                        }
                    }
                });
            } else if (videoPlayer.canPlayType('application/vnd.apple.mpegurl')) {
                videoPlayer.src = streamUrl;
                videoPlayer.addEventListener('loadedmetadata', () => {
                    transcodingOverlay.classList.add('hidden');
                    videoPlayer.classList.remove('hidden');
                    isStreamActive = true;
                    videoPlayer.play().catch(() => { });
                    showSeekBar();
                    showCustomControls();
                    updatePlayControls();
                    updateVolumeControls();
                    updateFullscreenControls();
                    schedulePlaybackChromeHide();
                }, { once: true });
            }
        } catch (err) {
            console.error(err);
            transcodingOverlay.innerHTML = `<p style="color: #ef4444;">Transcode Failed: ${err.message}</p>`;
        }
    };

    const stopTranscode = async () => {
        if (!currentVideoKey || !stopTranscodeBtn) return;
        stopTranscodeBtn.disabled = true;
        stopTranscodeBtn.textContent = 'Stopping...';

        try {
            const res = await fetch('/api/stop-transcode', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ key: currentVideoKey })
            });
            const data = await res.json();
            if (!res.ok) throw new Error(data.error || 'Failed to stop transcode');

            handleProgress({ status: 'cancelled', percent: 0, details: 'Transcode stopped' });
            if (hlsInstance) {
                hlsInstance.destroy();
                hlsInstance = null;
            }
            isStreamActive = false;
            videoPlayer.pause();
            videoPlayer.removeAttribute('src');
            videoPlayer.load();
            hideSeekBar();
            hideCustomControls();
            setPlaybackStatus('Paused', 'paused');
            updatePlayControls();
            if (transcodeBtn) {
                transcodeBtn.disabled = false;
                transcodeBtn.textContent = 'Start Transcode';
                transcodeBtn.classList.remove('hidden');
            }
            stopTranscodeBtn.classList.add('hidden');
        } catch (err) {
            console.error('Stop transcode failed:', err);
            alert(`Stop transcode failed: ${err.message}`);
        } finally {
            if (stopTranscodeBtn) {
                stopTranscodeBtn.disabled = false;
                stopTranscodeBtn.textContent = 'Stop Transcode';
            }
        }
    };

    const stopPolling = () => {
        if (currentPollInterval) {
            clearInterval(currentPollInterval);
            currentPollInterval = null;
        }
    };

    const playMp4Stream = (url) => {
        transcodingOverlay.classList.add('hidden');
        videoPlayer.classList.remove('hidden');
        hideCustomControls();
        if (playBtn) playBtn.classList.add('hidden');
        if (stopTranscodeBtn) {
            stopTranscodeBtn.classList.add('hidden');
        }
        resetPhases();

        videoPlayer.src = url;
        videoPlayer.load();
        isStreamActive = true;
        lastAbsolutePlaybackTime = seekOffset;
        showSeekBar();
        showCustomControls();
        videoPlayer.addEventListener('loadedmetadata', () => {
            updateSeekBarPosition(seekOffset);
            updatePlayControls();
            updateVolumeControls();
            updateFullscreenControls();
            schedulePlaybackChromeHide();
        }, { once: true });
    };

    if (controlPlayToggle) {
        controlPlayToggle.addEventListener('click', () => {
            if (!isStreamActive) return;
            if (videoPlayer.paused) {
                videoPlayer.play().catch(() => { });
            } else {
                videoPlayer.pause();
            }
        });
    }

    if (controlMuteToggle) {
        controlMuteToggle.addEventListener('click', () => {
            if (videoPlayer.muted || videoPlayer.volume === 0) {
                videoPlayer.muted = false;
                videoPlayer.volume = lastVolumeBeforeMute > 0 ? lastVolumeBeforeMute : 1;
            } else {
                lastVolumeBeforeMute = videoPlayer.volume > 0 ? videoPlayer.volume : lastVolumeBeforeMute;
                videoPlayer.muted = true;
                videoPlayer.volume = 0;
            }
            updateVolumeControls();
        });
    }

    if (volumeSlider) {
        volumeSlider.addEventListener('input', (event) => {
            const nextVolume = Math.max(0, Math.min(1, parseFloat(event.target.value)));
            videoPlayer.muted = nextVolume === 0;
            videoPlayer.volume = nextVolume;
            if (nextVolume > 0) {
                lastVolumeBeforeMute = nextVolume;
            }
            updateVolumeControls();
        });
    }

    if (playbackSpeed) {
        playbackSpeed.addEventListener('change', (event) => {
            const nextRate = Math.max(0.25, Math.min(4, parseFloat(event.target.value) || 1));
            videoPlayer.playbackRate = nextRate;
            updateSpeedControls();
            revealPlaybackChrome();
            schedulePlaybackChromeHide();
        });
    }

    if (controlFullscreenToggle) {
        controlFullscreenToggle.addEventListener('click', async () => {
            try {
                if (document.fullscreenElement) {
                    await document.exitFullscreen();
                } else if (playerOverlay?.parentElement && typeof playerOverlay.parentElement.requestFullscreen === 'function') {
                    await playerOverlay.parentElement.requestFullscreen();
                }
            } catch (error) {
                console.error('Fullscreen toggle failed:', error);
            } finally {
                updateFullscreenControls();
            }
        });
    }

    document.addEventListener('fullscreenchange', updateFullscreenControls);



    updatePlayControls();
    updateVolumeControls();
    updateFullscreenControls();
    updateSpeedControls();
    updateTranscodeProgressBar(0);

    // Bind events
    refreshBtn.addEventListener('click', () => fetchVideos(selectedBucket));
    if (clearDownloadCacheBtn) {
        clearDownloadCacheBtn.addEventListener('click', clearDownloadCache);
    }
    if (stopTranscodeBtn) {
        stopTranscodeBtn.addEventListener('click', stopTranscode);
    }
    if (loginBtn) {
        loginBtn.addEventListener('click', login);
    }
    if (bucketSelect) {
        bucketSelect.addEventListener('change', async (event) => {
            selectedBucket = event.target.value;
            await fetchVideos(selectedBucket);
        });
    }

    if (logoutBtn) {
        logoutBtn.addEventListener('click', async () => {
            const sessionId = localStorage.getItem('sessionId');
            if (sessionId) {
                try {
                    await fetch('/api/logout', { 
                        method: 'POST', 
                        headers: { 'X-Session-ID': sessionId } 
                    });
                } catch(e){}
            }
            clearSessionAuth();
            location.reload();
        });
    }

    if (themeSelector) {
        themeSelector.addEventListener('change', (e) => {
            const theme = e.target.value;
            document.documentElement.setAttribute('data-theme', theme);
            localStorage.setItem('theme', theme);
        });
    }

    const initApp = async () => {
        const savedSession = localStorage.getItem('sessionId');
        const savedUsername = localStorage.getItem('username');
        const savedTheme = localStorage.getItem('theme') || 'light';
        
        document.documentElement.setAttribute('data-theme', savedTheme);
        if (themeSelector) themeSelector.value = savedTheme;

        if (savedSession) {
            setSessionAuth(savedSession, savedUsername);
            try {
                const res = await fetch('/api/buckets', { headers: s3AuthHeaders });
                if (!res.ok) throw new Error('Session invalid');
                const data = await res.json();
                renderBuckets(data.buckets);
                showApp();
                selectedBucket = data.buckets[0].Name;
                if (bucketSelect) bucketSelect.value = selectedBucket;
                loadConfig();
                connectWebSocket();
                await fetchVideos(selectedBucket);
            } catch (err) {
                console.error('Auto login failed:', err);
                clearSessionAuth();
                showLogin();
            }
        } else {
            showLogin();
        }
    };

    // Initial state: attempt auto login
    initApp();
});
