/**
 * VideoKonverter TV - Video-Player v5.0
 * VKNative Bridge fuer Direct-Play (Tizen AVPlay / Android ExoPlayer),
 * HLS-Streaming mit hls.js als Fallback,
 * kompaktes Popup-Menue, Fullscreen-Player mit Audio/Untertitel/Qualitaets-Auswahl,
 * Naechste-Episode-Countdown und Tastatur/Fernbedienung-Steuerung.
 */

// === State ===
let videoEl = null;
let cfg = {};           // Konfiguration aus initPlayer()
let videoInfo = null;    // Audio/Subtitle-Tracks vom Server
let currentAudio = 0;
let currentSub = -1;     // -1 = aus
let currentQuality = "hd";
let currentSpeed = 1.0;
let progressBar = null;
let timeDisplay = null;
let playBtn = null;
let controlsTimer = null;
let saveTimer = null;
let controlsVisible = true;
let popupOpen = false;   // Popup-Menue offen?
let popupSection = null; // Aktive Popup-Sektion (null = Hauptmenue)
let nextCountdown = null;
let episodesWatched = 0;

// HLS-State
let hlsInstance = null;      // hls.js Instanz
let hlsSessionId = null;     // Aktive HLS-Session-ID
let hlsReady = false;        // HLS-Playback bereit?
let hlsSeekOffset = 0;       // Server-seitiger Seek: echte Position im Video
let clientCodecs = null;     // Vom Client unterstuetzte Video-Codecs
let hlsRetryCount = 0;       // Retry-Zaehler fuer gesamten Stream-Start
let loadingTimeout = null;   // Timeout fuer Loading-Spinner

// Native Player State (VKNative Bridge: Tizen AVPlay / Android ExoPlayer)
let useNativePlayer = false; // VKNative Direct-Play aktiv?
let nativePlayStarted = false; // VKNative _vkOnReady wurde aufgerufen?
let nativePlayTimeout = null;  // Master-Timeout fuer VKNative-Start

// Legacy AVPlay Direct-Play State (Rueckwaerts-Kompatibilitaet)
let useDirectPlay = false;   // Alter AVPlayBridge-Pfad aktiv?

// Audio-Kompressor (DynamicsCompressorNode)
let _audioCtx = null;
let _audioCompressorSetup = false;

/**
 * Player initialisieren
 * @param {Object} opts - Konfiguration
 */
function initPlayer(opts) {
    cfg = opts;
    currentQuality = opts.streamQuality || "hd";

    // Client-Codec-Erkennung (welche Video-Codecs kann dieser Browser?)
    clientCodecs = detectSupportedCodecs();
    console.info("Client-Codecs:", clientCodecs.join(", "));

    videoEl = document.getElementById("player-video");
    progressBar = document.getElementById("player-progress-bar");
    timeDisplay = document.getElementById("player-time");
    playBtn = document.getElementById("btn-play");

    if (!videoEl) return;

    // Prioritaet 1: VKNative Bridge (Tizen AVPlay / Android ExoPlayer)
    if (window.VKNative) {
        console.info("[Player] VKNative erkannt (Platform: " + window.VKNative.platform + ")");
        showLoading();
        // Master-Timeout: Falls VKNative nach 15s nicht gestartet hat -> HLS Fallback
        nativePlayTimeout = setTimeout(function() {
            if (!nativePlayStarted) {
                console.warn("[Player] VKNative Master-Timeout (15s) - Fallback auf HLS");
                _cleanupNativePlayer();
                _nativeFallbackToHLS(opts.startPos || 0);
            }
        }, 15000);
        _tryNativeDirectPlay(opts.startPos || 0);
    }
    // Prioritaet 2: Legacy AVPlayBridge (alte Tizen-App ohne VKNative)
    else if (isTizenTV() && typeof AVPlayBridge !== "undefined" && AVPlayBridge.init()) {
        showLoading();
        _tryDirectPlay(opts.startPos || 0).then(success => {
            if (!success) {
                console.info("[Player] AVPlay Fallback -> HLS");
                useDirectPlay = false;
                const infoReady = loadVideoInfo();
                startHLSStream(opts.startPos || 0);
                infoReady.then(() => updatePlayerButtons());
            }
        });
    }
    // Prioritaet 3: Browser/PWA - Direct-Play (MP4) bevorzugt, HLS als Fallback
    else {
        showLoading();
        const startPos = opts.startPos || 0;
        loadVideoInfo().then(() => {
            updatePlayerButtons();
            _tryBrowserDirectPlay(startPos);
        }).catch(() => {
            startHLSStream(startPos);
        });
    }

    // Events
    videoEl.addEventListener("timeupdate", onTimeUpdate);
    videoEl.addEventListener("play", onPlay);
    videoEl.addEventListener("pause", onPause);
    videoEl.addEventListener("ended", onEnded);
    videoEl.addEventListener("click", togglePlay);
    // Loading ausblenden sobald Video laeuft (mehrere Events als Sicherheit)
    videoEl.addEventListener("playing", onPlaying);
    videoEl.addEventListener("canplay", hideLoading);
    videoEl.addEventListener("loadeddata", hideLoading);
    // Video-Error: Automatisch Retry mit Fallback
    videoEl.addEventListener("error", onVideoError);

    // Controls UI
    playBtn.addEventListener("click", togglePlay);
    const btnFs = document.getElementById("btn-fullscreen");
    if (btnFs) btnFs.addEventListener("click", toggleFullscreen);
    document.getElementById("player-progress").addEventListener("click", onProgressClick);

    // Einstellungen-Button -> Popup-Hauptmenue
    const btnSettings = document.getElementById("btn-settings");
    if (btnSettings) btnSettings.addEventListener("click", () => togglePopup());

    // Direkt-Buttons: Audio, Untertitel, Qualitaet
    const btnAudio = document.getElementById("btn-audio");
    if (btnAudio) btnAudio.addEventListener("click", () => openPopupSection("audio"));
    const btnSubs = document.getElementById("btn-subs");
    if (btnSubs) btnSubs.addEventListener("click", () => openPopupSection("subs"));
    const btnQuality = document.getElementById("btn-quality");
    if (btnQuality) btnQuality.addEventListener("click", () => openPopupSection("quality"));

    // Naechste-Episode-Button
    const btnNext = document.getElementById("btn-next");
    if (btnNext) btnNext.addEventListener("click", playNextEpisode);

    // Naechste-Episode Overlay Buttons
    const btnNextPlay = document.getElementById("btn-next-play");
    if (btnNextPlay) btnNextPlay.addEventListener("click", playNextEpisode);
    const btnNextCancel = document.getElementById("btn-next-cancel");
    if (btnNextCancel) btnNextCancel.addEventListener("click", cancelNext);

    // Schaust du noch?
    const btnStillYes = document.getElementById("btn-still-yes");
    if (btnStillYes) btnStillYes.addEventListener("click", () => {
        document.getElementById("still-watching-overlay").style.display = "none";
        episodesWatched = 0;
        videoEl.play();
    });
    const btnStillNo = document.getElementById("btn-still-no");
    if (btnStillNo) btnStillNo.addEventListener("click", () => {
        saveProgress();
        goBack();
    });

    // Tastatur-Steuerung
    document.addEventListener("keydown", onKeyDown);
    document.addEventListener("mousemove", showControls);
    document.addEventListener("touchstart", showControls);

    // Fullscreen nur auf Desktop/Handy anzeigen (nicht auf Samsung TV)
    if (btnFs && isTizenTV()) {
        btnFs.style.display = "none";
    }

    scheduleHideControls();
    saveTimer = setInterval(saveProgress, 10000);
}

// === Erkennung: Samsung Tizen TV ===

function isTizenTV() {
    return typeof tizen !== "undefined" || /Tizen/i.test(navigator.userAgent);
}

// === Video-Info laden ===

async function loadVideoInfo() {
    try {
        const resp = await fetch(`/api/library/videos/${cfg.videoId}/info`);
        videoInfo = await resp.json();

        // Bevorzugte Audio-Spur finden
        if (videoInfo.audio_tracks) {
            const prefIdx = videoInfo.audio_tracks.findIndex(
                a => a.lang === cfg.preferredAudio);
            if (prefIdx >= 0) currentAudio = prefIdx;
        }

        // Bevorzugte Untertitel-Spur finden
        if (cfg.subtitlesEnabled && cfg.preferredSub && videoInfo.subtitle_tracks) {
            const subIdx = videoInfo.subtitle_tracks.findIndex(
                s => s.lang === cfg.preferredSub);
            if (subIdx >= 0) currentSub = subIdx;
        }

        // Untertitel-Tracks als <track> hinzufuegen
        if (videoInfo.subtitle_tracks) {
            videoInfo.subtitle_tracks.forEach((sub, i) => {
                const track = document.createElement("track");
                track.kind = "subtitles";
                track.src = `/api/library/videos/${cfg.videoId}/subtitles/${i}`;
                track.srclang = sub.lang || "und";
                track.label = langName(sub.lang) || `Spur ${i + 1}`;
                if (i === currentSub) track.default = true;
                videoEl.appendChild(track);
            });
            updateSubtitleTrack();
        }
    } catch (e) {
        console.warn("Video-Info laden fehlgeschlagen:", e);
    }
}

// === Codec-Erkennung ===

/**
 * Erkennt automatisch welche Video-Codecs der Browser/TV decodieren kann.
 * Wird beim HLS-Start an den Server geschickt -> Server entscheidet copy vs transcode.
 *
 * WICHTIG: Unterscheidung zwischen nativem HLS (Tizen/Safari) und MSE (hls.js):
 * - Natives HLS: canPlayType() meldet oft AV1/VP9, aber der native HLS-Player
 *   unterstuetzt diese Codecs NICHT zuverlaessig in fMP4-Segmenten.
 *   -> Konservativ: nur H.264 (+ evtl. HEVC)
 * - MSE/hls.js: MediaSource.isTypeSupported() ist zuverlaessig
 *   -> Alle unterstuetzten Codecs melden
 */
function detectSupportedCodecs() {
    // VKNative Bridge: Codec-Liste vom nativen Player abfragen
    if (window.VKNative && window.VKNative.getSupportedVideoCodecs) {
        var nativeCodecs = window.VKNative.getSupportedVideoCodecs();
        if (nativeCodecs && nativeCodecs.length) {
            console.info("[Player] VKNative Video-Codecs:", nativeCodecs.join(", "));
            return nativeCodecs;
        }
    }

    const codecs = [];
    const el = document.createElement("video");
    const hasNativeHLS = !!el.canPlayType("application/vnd.apple.mpegurl");
    const hasMSE = typeof MediaSource !== "undefined" && MediaSource.isTypeSupported;

    if (hasMSE) {
        // MSE-basiert: zuverlaessige Codec-Erkennung
        // (auch auf Tizen, da wir hls.js dem nativen Player vorziehen)
        if (MediaSource.isTypeSupported('video/mp4; codecs="avc1.640028"')) codecs.push("h264");
        if (MediaSource.isTypeSupported('video/mp4; codecs="hev1.1.6.L93.B0"')) codecs.push("hevc");
        if (MediaSource.isTypeSupported('video/mp4; codecs="av01.0.05M.08"')) codecs.push("av1");
        if (MediaSource.isTypeSupported('video/mp4; codecs="vp09.00.10.08"')) codecs.push("vp9");
    } else {
        // Kein MSE: konservativ nur H.264 + HEVC melden
        codecs.push("h264");
        if (el.canPlayType('video/mp4; codecs="hev1.1.6.L93.B0"')
            || el.canPlayType('video/mp4; codecs="hvc1.1.6.L93.B0"')) {
            codecs.push("hevc");
        }
    }

    if (!codecs.length) codecs.push("h264");
    return codecs;
}

// === Loading-Indikator ===

let loadingTimer = null;

function showLoading() {
    var el = document.getElementById("player-loading");
    if (el) {
        el.classList.remove("hidden");
        el.classList.remove("fade-out");
        el.style.display = "";
    }
    // Fallback: Loading nach 8 Sekunden ausblenden und Controls zeigen
    clearTimeout(loadingTimer);
    loadingTimer = setTimeout(function() {
        hideLoading();
        // Falls Video noch nicht spielt: Controls anzeigen damit User manuell starten kann
        if (videoEl && videoEl.paused) {
            showControls();
            if (playBtn) playBtn.innerHTML = "&#9654;";
        }
    }, 8000);
}
function hideLoading() {
    clearTimeout(loadingTimer);
    clearTimeout(loadingTimeout);
    loadingTimeout = null;
    var el = document.getElementById("player-loading");
    if (!el || el.classList.contains("fade-out")) return;
    // Sanfter Uebergang: Loading-Overlay blendet ueber 1.5s aus
    // Puffert gleichzeitig das Video waehrend der Ueberblendung
    el.classList.add("fade-out");
    setTimeout(function() {
        el.style.display = "none";
    }, 1600);
}

function onPlaying() {
    hideLoading();
    hlsRetryCount = 0; // Reset bei erfolgreichem Start
    // Audio-Kompressor aktivieren (nur bei Browser-Wiedergabe, nicht VKNative)
    if (cfg.audioCompressor && !useNativePlayer && videoEl) {
        _setupAudioCompressor(videoEl);
    }
}

function onVideoError() {
    // Video-Element Fehler: Retry oder Fallback
    var err = videoEl.error;
    var msg = err ? "Video-Fehler: Code " + err.code : "Video-Fehler";
    console.error(msg, err);

    if (hlsRetryCount < 2) {
        hlsRetryCount++;
        if (typeof showToast === "function")
            showToast("Stream-Fehler, Retry " + hlsRetryCount + "/2...", "error");
        var seekPos = getCurrentTime();
        setTimeout(function() { startHLSStream(seekPos); }, 1000 * hlsRetryCount);
    } else {
        if (typeof showToast === "function")
            showToast("Video konnte nicht gestartet werden", "error");
        hideLoading();
    }
}

// === AVPlay Direct-Play ===

/**
 * Versucht Direct-Play via AVPlay (nur auf Tizen).
 * Laedt erweiterte Video-Info vom Server und prueft Kompatibilitaet.
 * @returns {boolean} true wenn Direct-Play gestartet wurde
 */
async function _tryDirectPlay(startPosSec) {
    try {
        // Erweiterte Video-Info laden (inkl. audio_codecs, video_codec_normalized)
        const resp = await fetch(`/tv/api/video-info/${cfg.videoId}`);
        if (!resp.ok) return false;

        const info = await resp.json();
        videoInfo = info;  // Video-Info global setzen

        // Bevorzugte Audio-/Untertitel-Spur finden
        if (info.audio_tracks) {
            const prefIdx = info.audio_tracks.findIndex(
                a => a.lang === cfg.preferredAudio);
            if (prefIdx >= 0) currentAudio = prefIdx;
        }
        if (cfg.subtitlesEnabled && cfg.preferredSub && info.subtitle_tracks) {
            const subIdx = info.subtitle_tracks.findIndex(
                s => s.lang === cfg.preferredSub);
            if (subIdx >= 0) currentSub = subIdx;
        }

        // AVPlay Kompatibilitaet pruefen
        if (!AVPlayBridge.canPlay(info)) {
            console.info("[Player] Direct-Play nicht kompatibel");
            return false;
        }

        // Direct-Play starten
        useDirectPlay = true;
        const directUrl = info.direct_play_url;
        if (!directUrl) return false;

        // Video-Element verstecken, AVPlay-Object anzeigen
        if (videoEl) videoEl.style.display = "none";

        const ok = AVPlayBridge.play(directUrl, {
            seekMs: Math.floor(startPosSec * 1000),
            onReady: () => {
                hideLoading();
                showControls();
                scheduleHideControls();
            },
            onTimeUpdate: (ms) => {
                // Progress-Bar und Zeit-Anzeige aktualisieren
                const current = ms / 1000;
                const dur = getDuration();
                if (progressBar && dur > 0) {
                    progressBar.style.width = ((current / dur) * 100) + "%";
                }
                if (timeDisplay) {
                    timeDisplay.textContent = formatTime(current) + " / " + formatTime(dur);
                }
            },
            onComplete: () => {
                onEnded();
            },
            onError: (err) => {
                console.error("[Player] AVPlay Fehler:", err);
                if (typeof showToast === "function")
                    showToast("Direct-Play Fehler, wechsle zu HLS...", "error");
                // Fallback auf HLS
                _cleanupDirectPlay();
                useDirectPlay = false;
                if (videoEl) videoEl.style.display = "";
                startHLSStream(startPosSec);
            },
            onBuffering: (buffering) => {
                if (buffering) showLoading();
                else hideLoading();
            },
        });

        if (!ok) {
            _cleanupDirectPlay();
            return false;
        }

        updatePlayerButtons();
        return true;
    } catch (e) {
        console.error("[Player] Direct-Play Init fehlgeschlagen:", e);
        _cleanupDirectPlay();
        return false;
    }
}

/** AVPlay Direct-Play bereinigen */
function _cleanupDirectPlay() {
    if (typeof AVPlayBridge !== "undefined") {
        AVPlayBridge.stop();
    }
    useDirectPlay = false;
    if (videoEl) videoEl.style.display = "";
}

// === VKNative Direct-Play ===

/**
 * Versucht Direct-Play ueber VKNative Bridge (Tizen AVPlay / Android ExoPlayer).
 * Laedt Video-Info und prueft Kompatibilitaet. Faellt bei Fehler auf HLS zurueck.
 */
async function _tryNativeDirectPlay(startPosSec) {
    console.info("[Player] _tryNativeDirectPlay start (videoId=" + cfg.videoId + ", startPos=" + startPosSec + ")");
    try {
        // Erweiterte Video-Info laden
        console.info("[Player] Lade Video-Info...");
        var resp = await fetch("/tv/api/video-info/" + cfg.videoId);
        console.info("[Player] Video-Info Response: HTTP " + resp.status);
        if (!resp.ok) {
            console.warn("[Player] Video-Info nicht ladbar (HTTP " + resp.status + ")");
            _nativeFallbackToHLS(startPosSec);
            return;
        }

        var info = await resp.json();
        videoInfo = info;
        console.info("[Player] Video-Info geladen: " + (info.video_codec_normalized || "?") +
                     "/" + (info.container || "?") + ", Audio: " + (info.audio_codecs || []).join(","));

        // Bevorzugte Audio-/Untertitel-Spur finden
        if (info.audio_tracks) {
            var prefIdx = info.audio_tracks.findIndex(
                function(a) { return a.lang === cfg.preferredAudio; });
            if (prefIdx >= 0) currentAudio = prefIdx;
        }
        if (cfg.subtitlesEnabled && cfg.preferredSub && info.subtitle_tracks) {
            var subIdx = info.subtitle_tracks.findIndex(
                function(s) { return s.lang === cfg.preferredSub; });
            if (subIdx >= 0) currentSub = subIdx;
        }

        // Untertitel als <track> hinzufuegen
        if (info.subtitle_tracks) {
            info.subtitle_tracks.forEach(function(sub, i) {
                var track = document.createElement("track");
                track.kind = "subtitles";
                track.src = "/api/library/videos/" + cfg.videoId + "/subtitles/" + i;
                track.srclang = sub.lang || "und";
                track.label = langName(sub.lang) || "Spur " + (i + 1);
                if (i === currentSub) track.default = true;
                videoEl.appendChild(track);
            });
            updateSubtitleTrack();
        }

        // VKNative Kompatibilitaets-Pruefung
        var canPlay = window.VKNative.canDirectPlay(info);
        console.info("[Player] VKNative canDirectPlay: " + canPlay);
        if (!canPlay) {
            console.info("[Player] VKNative: Codec nicht kompatibel -> HLS Fallback");
            _nativeFallbackToHLS(startPosSec);
            return;
        }

        // VKNative Callbacks registrieren
        window._vkOnReady = function() {
            nativePlayStarted = true;
            clearTimeout(nativePlayTimeout);
            nativePlayTimeout = null;
            hideLoading();
            showControls();
            scheduleHideControls();
            console.info("[Player] VKNative Direct-Play gestartet");
        };
        window._vkOnTimeUpdate = function(ms) {
            var current = ms / 1000;
            var dur = getDuration();
            if (progressBar && dur > 0) {
                progressBar.style.width = ((current / dur) * 100) + "%";
            }
            if (timeDisplay) {
                timeDisplay.textContent = formatTime(current) + " / " + formatTime(dur);
            }
        };
        window._vkOnComplete = function() {
            onEnded();
        };
        window._vkOnError = function(msg) {
            console.error("[Player] VKNative Fehler:", msg);
            clearTimeout(nativePlayTimeout);
            nativePlayTimeout = null;
            if (typeof showToast === "function")
                showToast("Direct-Play Fehler, wechsle zu HLS...", "error");
            _cleanupNativePlayer();
            startHLSStream(startPosSec);
        };
        window._vkOnBuffering = function(buffering) {
            // Nur Loading-Text aendern, nicht den ganzen Spinner neu starten
            // (wuerde den Master-Timeout zuruecksetzen)
            var el = document.getElementById("player-loading");
            if (el) {
                var textEl = el.querySelector(".player-loading-text");
                if (textEl) textEl.textContent = buffering ? "Puffert..." : "Stream wird geladen...";
            }
        };
        window._vkOnPlayStateChanged = function(playing) {
            if (playing) {
                nativePlayStarted = true;
                clearTimeout(nativePlayTimeout);
                nativePlayTimeout = null;
                onPlay();
            } else {
                onPause();
            }
        };
        // Return/Back-Taste auf Fernbedienung -> zurueck ins Menue navigieren
        window._vkOnStopped = function() {
            saveProgress();
            _cleanupNativePlayer();
            goBack();
        };

        // Direct-Play starten
        var directUrl = info.direct_play_url;
        console.info("[Player] Direct-Play URL: " + directUrl);
        if (!directUrl) {
            console.warn("[Player] Keine Direct-Play-URL vorhanden");
            _nativeFallbackToHLS(startPosSec);
            return;
        }

        // Stream-Token holen (fuer AVPlay, das keinen Cookie-Jar hat)
        try {
            var tokenResp = await fetch("/tv/api/stream-token", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({ video_id: cfg.videoId }),
            });
            if (tokenResp.ok) {
                var tokenData = await tokenResp.json();
                if (tokenData.token) {
                    directUrl += (directUrl.indexOf("?") >= 0 ? "&" : "?") +
                        "token=" + encodeURIComponent(tokenData.token);
                    console.info("[Player] Stream-Token angehaengt");
                }
            }
        } catch (tokenErr) {
            console.warn("[Player] Stream-Token Fehler (ignoriert):", tokenErr);
        }

        useNativePlayer = true;
        document.body.classList.add("vknative-playing");
        console.info("[Player] VKNative.play() aufrufen...");
        var ok = window.VKNative.play(directUrl, info, {
            seekMs: Math.floor(startPosSec * 1000)
        });
        console.info("[Player] VKNative.play() Ergebnis: " + ok);

        if (!ok) {
            console.warn("[Player] VKNative.play() fehlgeschlagen");
            _cleanupNativePlayer();
            _nativeFallbackToHLS(startPosSec);
            return;
        }

        updatePlayerButtons();

    } catch (e) {
        console.error("[Player] VKNative Init fehlgeschlagen:", e);
        clearTimeout(nativePlayTimeout);
        nativePlayTimeout = null;
        _cleanupNativePlayer();
        _nativeFallbackToHLS(startPosSec);
    }
}

/** VKNative bereinigen */
function _cleanupNativePlayer() {
    console.info("[Player] _cleanupNativePlayer()");
    clearTimeout(nativePlayTimeout);
    nativePlayTimeout = null;
    if (window.VKNative) {
        try { window.VKNative.stop(); } catch (e) { console.warn("[Player] VKNative.stop() Fehler:", e); }
    }
    useNativePlayer = false;
    nativePlayStarted = false;
    document.body.classList.remove("vknative-playing");
    // Callbacks entfernen
    window._vkOnReady = null;
    window._vkOnTimeUpdate = null;
    window._vkOnComplete = null;
    window._vkOnError = null;
    window._vkOnBuffering = null;
    window._vkOnPlayStateChanged = null;
    window._vkOnStopped = null;
}

/** Fallback von VKNative auf HLS */
function _nativeFallbackToHLS(startPosSec) {
    console.info("[Player] Fallback auf HLS (startPos=" + startPosSec + ")");
    clearTimeout(nativePlayTimeout);
    nativePlayTimeout = null;

    // Android: ExoPlayer fuer HLS nutzen (WebView-Audio hat Probleme)
    if (window.VKNative && window.VKNative.playHLS) {
        console.info("[Player] VKNative.playHLS() verfuegbar - nutze ExoPlayer fuer HLS");
        _startNativeHLS(startPosSec);
        return;
    }

    // Kein VKNative HLS (Tizen etc.) -> WebView-HLS
    useNativePlayer = false;
    nativePlayStarted = false;
    document.body.classList.remove("vknative-playing");
    if (videoEl) videoEl.style.display = "";
    var infoReady = loadVideoInfo();
    startHLSStream(startPosSec);
    infoReady.then(function() { updatePlayerButtons(); });
}

/** HLS ueber VKNative (ExoPlayer) abspielen - Audio laeuft zuverlaessig */
async function _startNativeHLS(startPosSec) {
    try {
        // HLS-Session vom Server anfordern
        var hlsSeek = startPosSec > 0 ? Math.floor(startPosSec) : 0;
        var resp = await fetch("/tv/api/hls/start", {
            method: "POST",
            headers: {"Content-Type": "application/json"},
            body: JSON.stringify({
                video_id: cfg.videoId,
                quality: currentQuality,
                audio: currentAudio,
                // Native Player (AVPlay/ExoPlayer) koennen Surround -> Default "surround"
                sound: cfg.soundMode || "surround",
                t: hlsSeek,
                codecs: clientCodecs || ["h264"],
            }),
        });

        if (!resp.ok) {
            console.warn("[Player] HLS-Session fehlgeschlagen (HTTP " + resp.status + ")");
            useNativePlayer = false;
            nativePlayStarted = false;
            if (videoEl) videoEl.style.display = "";
            startHLSStream(startPosSec);
            return;
        }

        var data = await resp.json();
        hlsSessionId = data.session_id;
        var playlistUrl = data.playlist_url;
        hlsSeekOffset = hlsSeek;

        // VKNative Callbacks
        window._vkOnReady = function() {
            nativePlayStarted = true;
            clearTimeout(nativePlayTimeout);
            nativePlayTimeout = null;
            hideLoading();
            showControls();
            scheduleHideControls();
            console.info("[Player] VKNative HLS gestartet");
        };
        window._vkOnTimeUpdate = function(ms) {
            // ExoPlayer meldet HLS-Stream-Position + Server-Seek-Offset
            var current = hlsSeekOffset + (ms / 1000);
            var dur = getDuration();
            if (progressBar && dur > 0) {
                progressBar.style.width = ((current / dur) * 100) + "%";
            }
            if (timeDisplay) {
                timeDisplay.textContent = formatTime(current) + " / " + formatTime(dur);
            }
        };
        window._vkOnComplete = function() { onEnded(); };
        window._vkOnError = function(msg) {
            console.error("[Player] VKNative HLS Fehler:", msg);
            _cleanupNativePlayer();
            if (videoEl) videoEl.style.display = "";
            startHLSStream(startPosSec);
        };
        window._vkOnBuffering = function(buffering) {
            if (buffering) showLoading();
            else hideLoading();
        };
        window._vkOnPlayStateChanged = function(playing) {
            if (playing) {
                nativePlayStarted = true;
                clearTimeout(nativePlayTimeout);
                nativePlayTimeout = null;
                onPlay();
            } else {
                onPause();
            }
        };

        // ExoPlayer HLS starten
        useNativePlayer = true;
        document.body.classList.add("vknative-playing");
        var ok = window.VKNative.playHLS(playlistUrl, {});
        console.info("[Player] VKNative.playHLS() Ergebnis: " + ok);

        if (!ok) {
            _cleanupNativePlayer();
            if (videoEl) videoEl.style.display = "";
            startHLSStream(startPosSec);
            return;
        }

        loadVideoInfo().then(function() { updatePlayerButtons(); });

    } catch (e) {
        console.error("[Player] _startNativeHLS Fehler:", e);
        _cleanupNativePlayer();
        if (videoEl) videoEl.style.display = "";
        startHLSStream(startPosSec);
    }
}

// === Audio-Kompressor (Client-seitige Lautstaerke-Nivellierung) ===

/**
 * DynamicsCompressorNode: Reduziert Lautstaerke-Schwankungen im Browser.
 * Laute Passagen (Explosionen) werden komprimiert, leise (Dialoge) angehoben.
 * Funktioniert nur bei <video>-Element (nicht bei VKNative/AVPlay).
 */
function _setupAudioCompressor(el) {
    if (_audioCompressorSetup || !el) return;
    try {
        _audioCtx = new (window.AudioContext || window.webkitAudioContext)();
        var source = _audioCtx.createMediaElementSource(el);
        var compressor = _audioCtx.createDynamicsCompressor();
        compressor.threshold.value = -30;  // Ab -30dB komprimieren
        compressor.knee.value = 20;        // Weicher Uebergang
        compressor.ratio.value = 4;        // 4:1 Kompression
        compressor.attack.value = 0.005;   // 5ms Ansprechzeit
        compressor.release.value = 0.1;    // 100ms Abklingzeit
        source.connect(compressor);
        compressor.connect(_audioCtx.destination);
        _audioCompressorSetup = true;
        console.info("[Player] Audio-Kompressor aktiviert (DynamicsCompressor)");
    } catch (e) {
        console.warn("[Player] Audio-Kompressor nicht verfuegbar:", e.message || e);
    }
}

// === Browser Direct-Play (MP4 mit Range-Requests) ===

/**
 * Versucht direkte MP4-Wiedergabe im Browser (PWA).
 * Faellt auf HLS zurueck wenn Codec nicht unterstuetzt oder Fehler.
 */
function _tryBrowserDirectPlay(seekSec) {
    if (!videoInfo || !videoInfo.direct_play_url) {
        console.info("[Player] Keine Direct-Play-URL, Fallback auf HLS");
        startHLSStream(seekSec);
        return;
    }

    var directUrl = videoInfo.direct_play_url;
    console.info("[Player] Browser Direct-Play: " + directUrl);

    // Pruefen ob der Browser den Codec abspielen kann
    var codec = (videoInfo.video_codec || "").toLowerCase();
    var canPlay = false;
    if (codec === "h264" || codec === "avc1") {
        canPlay = !!videoEl.canPlayType('video/mp4; codecs="avc1.640028"');
    } else if (codec === "hevc" || codec === "h265" || codec === "hvc1") {
        canPlay = !!videoEl.canPlayType('video/mp4; codecs="hvc1.1.6.L120"');
    } else if (codec === "av1") {
        canPlay = !!videoEl.canPlayType('video/mp4; codecs="av01.0.08M.08"');
    } else {
        // Unbekannter Codec - einfach versuchen
        canPlay = true;
    }

    if (!canPlay) {
        console.info("[Player] Browser unterstuetzt Codec '" + codec + "' nicht, Fallback auf HLS");
        startHLSStream(seekSec);
        return;
    }

    // Direct-Play starten
    useDirectPlay = true;
    videoEl.src = directUrl;

    // Seek-Position setzen sobald Metadaten geladen
    if (seekSec > 0) {
        videoEl.addEventListener("loadedmetadata", function() {
            videoEl.currentTime = seekSec;
        }, { once: true });
    }

    // Fehler-Handler: Bei Fehler auf HLS wechseln
    var directErrorHandler = function(e) {
        console.warn("[Player] Direct-Play Fehler, wechsle zu HLS:", e);
        videoEl.removeEventListener("error", directErrorHandler);
        useDirectPlay = false;
        videoEl.removeAttribute("src");
        videoEl.load();
        startHLSStream(seekSec);
    };
    videoEl.addEventListener("error", directErrorHandler, { once: true });

    // Abspielen sobald bereit
    videoEl.addEventListener("canplay", function() {
        videoEl.removeEventListener("error", directErrorHandler);
    }, { once: true });

    videoEl.play().catch(function(e) {
        console.warn("[Player] Autoplay blockiert (Direct-Play):", e);
        hideLoading();
        showControls();
        if (playBtn) playBtn.innerHTML = "&#9654;";
    });
}


// === HLS Streaming ===

async function startHLSStream(seekSec) {
    // Loading-Spinner anzeigen
    showLoading();

    // Vorherige Session beenden
    await cleanupHLS();

    // Seek-Offset merken (ffmpeg -ss schneidet serverseitig)
    hlsSeekOffset = seekSec > 0 ? Math.floor(seekSec) : 0;

    // Neue HLS-Session vom Server anfordern
    try {
        const resp = await fetch("/tv/api/hls/start", {
            method: "POST",
            headers: {"Content-Type": "application/json"},
            body: JSON.stringify({
                video_id: cfg.videoId,
                quality: currentQuality,
                audio: currentAudio,
                sound: cfg.soundMode || "stereo",
                t: hlsSeekOffset,
                codecs: clientCodecs || ["h264"],
            }),
        });

        if (!resp.ok) {
            console.error("HLS Session Start fehlgeschlagen:", resp.status);
            if (typeof showToast === "function")
                showToast("HLS-Start fehlgeschlagen (HTTP " + resp.status + ")", "error");
            setStreamUrlLegacy(seekSec);
            return;
        }

        const data = await resp.json();
        hlsSessionId = data.session_id;
        const playlistUrl = data.playlist_url;

        // Retry-Zaehler fuer Netzwerkfehler
        let networkRetries = 0;
        const MAX_RETRIES = 3;

        // HLS abspielen - hls.js bevorzugen (bessere A/V-Sync-Korrektur
        // als native HLS-Player, besonders bei fMP4-Remux von WebM-Quellen)
        if (typeof Hls !== "undefined" && Hls.isSupported()) {
            // hls.js (bevorzugt - funktioniert zuverlaessig auf allen Plattformen)
            hlsInstance = new Hls({
                maxBufferLength: 60,           // 60s Vorpuffer (mehr Reserve)
                maxMaxBufferLength: 120,       // Max 120s vorpuffern
                maxBufferSize: 200 * 1024 * 1024,  // 200 MB RAM-Limit
                backBufferLength: 30,          // Bereits gesehene 30s behalten
                startLevel: -1,
                fragLoadPolicy: {
                    default: {
                        maxTimeToFirstByteMs: 10000,
                        maxLoadTimeMs: 30000,      // 30s fuer grosse Segmente (AV1 copy: 20-34 MB)
                        timeoutRetry: { maxNumRetry: 3, retryDelayMs: 1000, maxRetryDelayMs: 8000 },
                        errorRetry: { maxNumRetry: 3, retryDelayMs: 1000, maxRetryDelayMs: 8000 },
                    },
                },
            });
            hlsInstance.loadSource(playlistUrl);
            hlsInstance.attachMedia(videoEl);

            hlsInstance.on(Hls.Events.MANIFEST_PARSED, () => {
                hlsReady = true;
                videoEl.addEventListener("playing", hideLoading, {once: true});
                videoEl.play().catch(e => {
                    console.warn("Autoplay blockiert:", e);
                    hideLoading();
                    showControls();
                    if (playBtn) playBtn.innerHTML = "&#9654;";
                });
            });

            hlsInstance.on(Hls.Events.ERROR, (event, data) => {
                if (data.fatal) {
                    console.error("HLS fataler Fehler:", data.type, data.details);
                    if (data.type === Hls.ErrorTypes.NETWORK_ERROR
                        && networkRetries < MAX_RETRIES) {
                        // Netzwerkfehler -> Retry mit Backoff
                        networkRetries++;
                        if (typeof showToast === "function")
                            showToast("Netzwerkfehler, Retry " +
                                networkRetries + "/" + MAX_RETRIES + "...", "error");
                        setTimeout(() => hlsInstance.startLoad(),
                            1000 * networkRetries);
                    } else {
                        // Zu viele Retries oder anderer Fehler -> Fallback
                        if (typeof showToast === "function")
                            showToast("Stream-Fehler: " + data.details, "error");
                        cleanupHLS();
                        setStreamUrlLegacy(seekSec);
                    }
                }
            });
        } else if (videoEl.canPlayType("application/vnd.apple.mpegurl")) {
            // Nativer HLS-Player (Fallback wenn hls.js nicht verfuegbar)
            videoEl.src = playlistUrl;
            hlsReady = true;
            videoEl.addEventListener("playing", hideLoading, {once: true});
            videoEl.play().catch(e => {
                console.warn("Autoplay blockiert (nativ):", e);
                hideLoading();
                showControls();
                if (playBtn) playBtn.innerHTML = "&#9654;";
            });
        } else {
            // Kein HLS moeglich -> Fallback
            console.warn("Weder hls.js noch natives HLS verfuegbar");
            setStreamUrlLegacy(seekSec);
        }
    } catch (e) {
        console.error("HLS Start Fehler:", e);
        if (typeof showToast === "function")
            showToast("Stream-Start fehlgeschlagen: " + e.message, "error");
        hideLoading();
        setStreamUrlLegacy(seekSec);
    }
}

/** Fallback: Altes Pipe-Streaming (fMP4 ueber StreamResponse) */
function setStreamUrlLegacy(seekSec) {
    const params = new URLSearchParams({
        quality: currentQuality,
        audio: currentAudio,
        sound: cfg.soundMode || "stereo",
    });
    if (seekSec > 0) params.set("t", Math.floor(seekSec));
    videoEl.src = `/api/library/videos/${cfg.videoId}/stream?${params}`;
    videoEl.addEventListener("playing", hideLoading, {once: true});
    videoEl.play().catch(e => {
        console.warn("Autoplay blockiert (Legacy):", e);
        hideLoading();
        showControls();
        if (playBtn) playBtn.innerHTML = "&#9654;";
    });
}

/** HLS aufraumen: hls.js + Server-Session beenden */
async function cleanupHLS() {
    if (hlsInstance) {
        hlsInstance.destroy();
        hlsInstance = null;
    }
    if (hlsSessionId) {
        // Server-Session loeschen (fire & forget)
        fetch(`/tv/api/hls/${hlsSessionId}`, {method: "DELETE"}).catch(() => {});
        hlsSessionId = null;
    }
    hlsReady = false;
    hlsSeekOffset = 0;
}

// === Playback-Controls ===

function togglePlay() {
    if (useNativePlayer && window.VKNative) {
        window.VKNative.togglePlay();
        // PlayStateChanged-Callback aktualisiert Icon automatisch
        return;
    }
    if (useDirectPlay && typeof AVPlayBridge !== "undefined") {
        AVPlayBridge.togglePlay();
        if (AVPlayBridge.isPlaying()) onPlay();
        else onPause();
        return;
    }
    if (!videoEl) return;
    if (videoEl.paused) videoEl.play();
    else videoEl.pause();
}

function onPlay() {
    if (playBtn) playBtn.innerHTML = "&#10074;&#10074;";
    scheduleHideControls();
}

function onPause() {
    if (playBtn) playBtn.innerHTML = "&#9654;";
    showControls();
    saveProgress();
}

async function onEnded() {
    // Fortschritt + Watch-Status speichern (wartet auf API-Antwort)
    await saveProgress(true);
    episodesWatched++;

    // Schaust du noch? (wenn Max-Episoden erreicht)
    if (cfg.autoplayMax > 0 && episodesWatched >= cfg.autoplayMax) {
        document.getElementById("still-watching-overlay").style.display = "";
        return;
    }

    // Player bereinigen
    if (useNativePlayer) _cleanupNativePlayer();
    if (useDirectPlay) _cleanupDirectPlay();
    cleanupHLS();

    // Zurueck zur Seriendetail-Seite navigieren
    if (cfg.seriesDetailUrl) {
        if (cfg.nextVideoId && cfg.autoplay) {
            // Autoplay: Serie mit Countdown auf naechster Episode anzeigen
            window.location.href = cfg.seriesDetailUrl +
                "?post_play=1&next_video=" + cfg.nextVideoId +
                "&countdown=" + (cfg.autoplayCountdown || 10);
        } else {
            // Kein Autoplay: Serie zeigen, zur letzten Episode scrollen
            window.location.href = cfg.seriesDetailUrl +
                "?last_watched=" + cfg.videoId;
        }
    } else {
        // Kein Serien-Kontext (Film etc.) -> einfach zurueck
        goBack();
    }
}

// === Seeking ===

function seekRelative(seconds) {
    if (useNativePlayer && window.VKNative) {
        let cur = getCurrentTime();
        let dur = getDuration();
        let newMs = Math.max(0, Math.min((cur + seconds) * 1000, dur * 1000));
        window.VKNative.seek(newMs);
        showControls();
        return;
    }
    if (useDirectPlay && typeof AVPlayBridge !== "undefined") {
        const cur = getCurrentTime();
        const dur = getDuration();
        const newMs = Math.max(0, Math.min((cur + seconds) * 1000, dur * 1000));
        AVPlayBridge.seek(newMs);
        showControls();
        return;
    }
    if (!videoEl) return;
    const dur = getDuration();
    const cur = getCurrentTime();
    const newTime = Math.max(0, Math.min(cur + seconds, dur));

    if (hlsSessionId) {
        // HLS: nativen Seek verwenden (hls.js unterstuetzt das)
        videoEl.currentTime = Math.max(0, Math.min(
            videoEl.currentTime + seconds, videoEl.duration || Infinity));
        showControls();
    } else {
        // Legacy: neuen Stream starten
        startHLSStream(newTime);
        showControls();
    }
}

function onProgressClick(e) {
    const rect = e.currentTarget.getBoundingClientRect();
    const pct = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
    const dur = getDuration();
    if (!dur) return;

    const seekTo = pct * dur;

    if (useNativePlayer && window.VKNative) {
        window.VKNative.seek(seekTo * 1000);
        showControls();
        return;
    }
    if (useDirectPlay && typeof AVPlayBridge !== "undefined") {
        AVPlayBridge.seek(seekTo * 1000);
        showControls();
        return;
    }

    if (!videoEl) return;
    // HLS: Immer neuen Stream starten (server-seitiger Seek)
    startHLSStream(seekTo);
    showControls();
}

// === Zeit-Funktionen ===

function getCurrentTime() {
    if (useNativePlayer && window.VKNative) {
        return window.VKNative.getCurrentTime() / 1000;  // ms -> sec
    }
    if (useDirectPlay && typeof AVPlayBridge !== "undefined") {
        return AVPlayBridge.getCurrentTime() / 1000;  // ms -> sec
    }
    if (!videoEl) return 0;
    // Bei HLS mit Server-Seek: videoEl.currentTime + Offset = echte Position
    return hlsSeekOffset + (videoEl.currentTime || 0);
}

function getDuration() {
    // Echte Gesamtdauer des Videos (nicht der HLS-Stream-Dauer)
    return cfg.duration || 0;
}

function onTimeUpdate() {
    if (!videoEl) return;
    const current = getCurrentTime();
    const dur = getDuration();

    if (progressBar && dur > 0) {
        progressBar.style.width = ((current / dur) * 100) + "%";
    }
    if (timeDisplay) {
        timeDisplay.textContent = formatTime(current) + " / " + formatTime(dur);
    }
}

function formatTime(sec) {
    if (!sec || !isFinite(sec)) return "0:00";
    const h = Math.floor(sec / 3600);
    const m = Math.floor((sec % 3600) / 60);
    const s = Math.floor(sec % 60);
    if (h > 0) return h + ":" + String(m).padStart(2, "0") + ":" + String(s).padStart(2, "0");
    return m + ":" + String(s).padStart(2, "0");
}

// === Controls Ein-/Ausblenden ===

function showControls() {
    const wrapper = document.getElementById("player-wrapper");
    if (wrapper) wrapper.classList.remove("player-hide-controls");
    controlsVisible = true;
    scheduleHideControls();
}

function hideControls() {
    // Bei VKNative: Controls ausblenden wenn abgespielt wird
    if (useNativePlayer && window.VKNative) {
        if (!window.VKNative.isPlaying() || popupOpen) return;
    } else if (!videoEl || videoEl.paused || popupOpen) return;
    const wrapper = document.getElementById("player-wrapper");
    if (wrapper) wrapper.classList.add("player-hide-controls");
    controlsVisible = false;
}

function scheduleHideControls() {
    if (controlsTimer) clearTimeout(controlsTimer);
    controlsTimer = setTimeout(hideControls, 4000);
}

// === Fullscreen ===

function toggleFullscreen() {
    const wrapper = document.getElementById("player-wrapper");
    if (!document.fullscreenElement) {
        (wrapper || document.documentElement).requestFullscreen().catch(() => {});
    } else {
        document.exitFullscreen().catch(() => {});
    }
}

// === Popup-Menue (ersetzt das grosse Overlay-Panel) ===

function togglePopup() {
    if (popupOpen) {
        closePopup();
    } else {
        openPopupSection(null);
    }
}

function openPopupSection(section) {
    const popup = document.getElementById("player-popup");
    if (!popup) return;

    if (popupOpen && popupSection === section) {
        // Gleiche Sektion nochmal -> schliessen
        closePopup();
        return;
    }

    popupOpen = true;
    popupSection = section;
    popup.style.display = "";
    popup.classList.add("popup-visible");
    renderPopup(section);
    showControls();

    // Focus auf ersten Button im Popup
    requestAnimationFrame(() => {
        const first = popup.querySelector("[data-focusable]");
        if (first) first.focus();
    });
}

function closePopup() {
    const popup = document.getElementById("player-popup");
    if (!popup) return;
    popupOpen = false;
    popupSection = null;
    popup.classList.remove("popup-visible");
    popup.style.display = "none";
}

function renderPopup(section) {
    const popup = document.getElementById("player-popup");
    if (!popup) return;

    let html = "";

    if (!section) {
        // Hauptmenue: Liste aller Optionen
        html = '<div class="popup-menu">';

        // Audio
        const audioLabel = _currentAudioLabel();
        html += `<button class="popup-menu-item" data-focusable onclick="openPopupSection('audio')">
            <span class="popup-item-label">Audio</span>
            <span class="popup-item-value">${audioLabel}</span>
        </button>`;

        // Untertitel
        const subLabel = currentSub >= 0 ? _currentSubLabel() : "Aus";
        html += `<button class="popup-menu-item" data-focusable onclick="openPopupSection('subs')">
            <span class="popup-item-label">Untertitel</span>
            <span class="popup-item-value">${subLabel}</span>
        </button>`;

        // Qualitaet
        const qualLabels = {uhd: "Ultra HD", hd: "HD", sd: "SD", low: "Niedrig"};
        html += `<button class="popup-menu-item" data-focusable onclick="openPopupSection('quality')">
            <span class="popup-item-label">Qualit\u00e4t</span>
            <span class="popup-item-value">${qualLabels[currentQuality] || "HD"}</span>
        </button>`;

        // Geschwindigkeit
        html += `<button class="popup-menu-item" data-focusable onclick="openPopupSection('speed')">
            <span class="popup-item-label">Geschwindigkeit</span>
            <span class="popup-item-value">${currentSpeed}x</span>
        </button>`;

        html += "</div>";
    } else if (section === "audio") {
        html = _renderAudioOptions();
    } else if (section === "subs") {
        html = _renderSubOptions();
    } else if (section === "quality") {
        html = _renderQualityOptions();
    } else if (section === "speed") {
        html = _renderSpeedOptions();
    }

    popup.innerHTML = html;
}

function _currentAudioLabel() {
    if (videoInfo && videoInfo.audio_tracks && videoInfo.audio_tracks[currentAudio]) {
        const a = videoInfo.audio_tracks[currentAudio];
        const ch = a.channels > 2 ? ` ${a.channels}ch` : "";
        return langName(a.lang) + ch;
    }
    return "Spur 1";
}

function _currentSubLabel() {
    if (videoInfo && videoInfo.subtitle_tracks && videoInfo.subtitle_tracks[currentSub]) {
        return langName(videoInfo.subtitle_tracks[currentSub].lang);
    }
    return "Spur " + (currentSub + 1);
}

function _renderAudioOptions() {
    let html = '<div class="popup-submenu">';
    html += `<button class="popup-back" data-focusable onclick="openPopupSection(null)">&larr; Audio</button>`;
    if (videoInfo && videoInfo.audio_tracks) {
        videoInfo.audio_tracks.forEach((a, i) => {
            const label = langName(a.lang) || `Spur ${i + 1}`;
            const ch = a.channels > 2 ? ` (${a.channels}ch)` : "";
            const active = i === currentAudio ? " active" : "";
            html += `<button class="popup-option${active}" data-focusable onclick="switchAudio(${i})">${label}${ch}</button>`;
        });
    }
    html += "</div>";
    return html;
}

function _renderSubOptions() {
    let html = '<div class="popup-submenu">';
    html += `<button class="popup-back" data-focusable onclick="openPopupSection(null)">&larr; Untertitel</button>`;
    html += `<button class="popup-option${currentSub === -1 ? ' active' : ''}" data-focusable onclick="switchSub(-1)">Aus</button>`;
    if (videoInfo && videoInfo.subtitle_tracks) {
        videoInfo.subtitle_tracks.forEach((s, i) => {
            const label = langName(s.lang) || `Spur ${i + 1}`;
            const active = i === currentSub ? " active" : "";
            html += `<button class="popup-option${active}" data-focusable onclick="switchSub(${i})">${label}</button>`;
        });
    }
    html += "</div>";
    return html;
}

function _renderQualityOptions() {
    const qualities = [
        ["uhd", "Ultra HD"], ["hd", "HD"],
        ["sd", "SD"], ["low", "Niedrig"]
    ];
    let html = '<div class="popup-submenu">';
    html += `<button class="popup-back" data-focusable onclick="openPopupSection(null)">&larr; Qualit\u00e4t</button>`;
    qualities.forEach(([val, label]) => {
        const active = val === currentQuality ? " active" : "";
        html += `<button class="popup-option${active}" data-focusable onclick="switchQuality('${val}')">${label}</button>`;
    });
    html += "</div>";
    return html;
}

function _renderSpeedOptions() {
    const speeds = [0.5, 0.75, 1.0, 1.25, 1.5, 2.0];
    let html = '<div class="popup-submenu">';
    html += `<button class="popup-back" data-focusable onclick="openPopupSection(null)">&larr; Geschwindigkeit</button>`;
    speeds.forEach(s => {
        const active = s === currentSpeed ? " active" : "";
        html += `<button class="popup-option${active}" data-focusable onclick="switchSpeed(${s})">${s}x</button>`;
    });
    html += "</div>";
    return html;
}

// === Audio/Sub/Quality/Speed wechseln ===

function switchAudio(idx) {
    if (idx === currentAudio) return;
    currentAudio = idx;
    const currentTime = getCurrentTime();

    if (useNativePlayer && window.VKNative) {
        // VKNative: Audio-Track wechseln versuchen
        var ok = window.VKNative.setAudioTrack(idx);
        if (ok) {
            // Erfolg (z.B. Android ExoPlayer kann Track direkt wechseln)
            renderPopup(popupSection);
            updatePlayerButtons();
            return;
        }
        // Nicht moeglich (z.B. Tizen AVPlay) -> HLS Fallback
        console.info("[Player] VKNative Audio-Wechsel nicht moeglich -> HLS Fallback");
        _cleanupNativePlayer();
        if (videoEl) videoEl.style.display = "";
    }
    if (useDirectPlay) {
        console.info("[Player] Audio-Wechsel -> HLS Fallback");
        _cleanupDirectPlay();
        useDirectPlay = false;
        if (videoEl) videoEl.style.display = "";
    }
    // Neuen HLS-Stream mit anderer Audio-Spur starten
    startHLSStream(currentTime);
    renderPopup(popupSection);
    updatePlayerButtons();
}

function switchSub(idx) {
    currentSub = idx;
    updateSubtitleTrack();
    renderPopup(popupSection);
    updatePlayerButtons();
}

function updateSubtitleTrack() {
    if (!videoEl || !videoEl.textTracks) return;
    for (let i = 0; i < videoEl.textTracks.length; i++) {
        videoEl.textTracks[i].mode = (i === currentSub) ? "showing" : "hidden";
    }
}

function switchQuality(q) {
    if (q === currentQuality) return;
    currentQuality = q;
    const currentTime = getCurrentTime();
    startHLSStream(currentTime);
    renderPopup(popupSection);
    updatePlayerButtons();
}

function switchSpeed(s) {
    currentSpeed = s;
    if (useNativePlayer && window.VKNative) {
        window.VKNative.setPlaybackSpeed(s);
    }
    if (videoEl) videoEl.playbackRate = s;
    renderPopup(popupSection);
}

// === Navigation ===

/** Zurueck mit Fade-Out-Animation */
function goBack() {
    var wrapper = document.getElementById("player-wrapper");
    if (wrapper) {
        wrapper.style.transition = "opacity 0.3s ease";
        wrapper.style.opacity = "0";
    }
    setTimeout(function() { window.history.back(); }, 300);
}

// === Naechste Episode ===

function showNextEpisodeOverlay() {
    const overlay = document.getElementById("next-overlay");
    if (!overlay) return;
    overlay.style.display = "";
    let remaining = cfg.autoplayCountdown || 10;
    const countdownEl = document.getElementById("next-countdown");

    nextCountdown = setInterval(() => {
        remaining--;
        if (countdownEl) countdownEl.textContent = remaining + "s";
        if (remaining <= 0) {
            clearInterval(nextCountdown);
            playNextEpisode();
        }
    }, 1000);
    if (countdownEl) countdownEl.textContent = remaining + "s";
}

async function playNextEpisode() {
    if (nextCountdown) clearInterval(nextCountdown);
    if (useNativePlayer) _cleanupNativePlayer();
    if (useDirectPlay) _cleanupDirectPlay();
    await cleanupHLS();
    if (cfg.nextUrl) window.location.href = cfg.nextUrl;
}

function cancelNext() {
    if (nextCountdown) clearInterval(nextCountdown);
    const overlay = document.getElementById("next-overlay");
    if (overlay) overlay.style.display = "none";
    goBack();
}

// === D-Pad Navigation fuer Fernbedienung ===

function _getFocusables() {
    if (popupOpen) {
        const popup = document.getElementById("player-popup");
        return popup ? Array.from(popup.querySelectorAll("[data-focusable]")) : [];
    }
    // "Naechste Episode" oder "Schaust du noch" Overlay?
    const nextOv = document.getElementById("next-overlay");
    if (nextOv && nextOv.style.display !== "none") {
        return Array.from(nextOv.querySelectorAll("[data-focusable]"));
    }
    const stillOv = document.getElementById("still-watching-overlay");
    if (stillOv && stillOv.style.display !== "none") {
        return Array.from(stillOv.querySelectorAll("[data-focusable]"));
    }
    // Player-Controls
    const controls = document.getElementById("player-controls");
    return controls ? Array.from(controls.querySelectorAll("[data-focusable]")) : [];
}

function _focusNext(direction) {
    const items = _getFocusables();
    if (!items.length) return false;
    const cur = items.indexOf(document.activeElement);
    let next;
    if (direction === 1) {
        next = cur < 0 ? 0 : Math.min(cur + 1, items.length - 1);
    } else {
        next = cur < 0 ? items.length - 1 : Math.max(cur - 1, 0);
    }
    items[next].focus();
    return true;
}

// === Tastatur-Steuerung ===

function onKeyDown(e) {
    // Samsung Tizen Remote Keys + Android TV Media Keys
    const keyMap = {
        10009: "Escape", 10182: "Escape",
        415: "Play", 19: "Pause", 413: "Stop",
        417: "FastForward", 412: "Rewind",
        // Samsung Farbtasten
        403: "ColorRed", 404: "ColorGreen",
        405: "ColorYellow", 406: "ColorBlue",
        // Android TV Media Keys
        85: "Play",          // KEYCODE_MEDIA_PLAY_PAUSE
        126: "Play",         // KEYCODE_MEDIA_PLAY
        127: "Pause",        // KEYCODE_MEDIA_PAUSE
        86: "Stop",          // KEYCODE_MEDIA_STOP
        87: "FastForward",   // KEYCODE_MEDIA_NEXT
        88: "Rewind",        // KEYCODE_MEDIA_PREVIOUS
        90: "FastForward",   // KEYCODE_MEDIA_FAST_FORWARD
        89: "Rewind",        // KEYCODE_MEDIA_REWIND
    };
    const key = keyMap[e.keyCode] || e.key;

    const active = document.activeElement;
    const buttonFocused = active && active.hasAttribute("data-focusable") &&
        active.tagName === "BUTTON";

    // --- Popup offen: D-Pad navigiert im Popup ---
    if (popupOpen) {
        switch (key) {
            case "Escape": case "Backspace":
                if (popupSection) {
                    // Zurueck zum Hauptmenue
                    openPopupSection(null);
                } else {
                    closePopup();
                    const btnSettings = document.getElementById("btn-settings");
                    if (btnSettings) btnSettings.focus();
                }
                e.preventDefault(); return;
            case "ArrowUp":
                _focusNext(-1); e.preventDefault(); return;
            case "ArrowDown":
                _focusNext(1); e.preventDefault(); return;
            case "ArrowLeft":
                if (popupSection) {
                    openPopupSection(null);
                } else {
                    closePopup();
                }
                e.preventDefault(); return;
            case "ArrowRight":
            case "Enter":
                if (buttonFocused) active.click();
                e.preventDefault(); return;
        }
    }

    // --- "Naechste Episode" / "Schaust du noch" Overlay ---
    const nextOv = document.getElementById("next-overlay");
    const stillOv = document.getElementById("still-watching-overlay");
    const modalOpen = (nextOv && nextOv.style.display !== "none") ||
                      (stillOv && stillOv.style.display !== "none");
    if (modalOpen) {
        switch (key) {
            case "ArrowLeft": case "ArrowRight":
                _focusNext(key === "ArrowRight" ? 1 : -1);
                e.preventDefault(); return;
            case "Enter":
                if (buttonFocused) active.click();
                e.preventDefault(); return;
        }
    }

    // --- Controls sichtbar + Button fokussiert: D-Pad navigiert ---
    if (controlsVisible && buttonFocused) {
        switch (key) {
            case "ArrowLeft":
                _focusNext(-1); showControls(); e.preventDefault(); return;
            case "ArrowRight":
                _focusNext(1); showControls(); e.preventDefault(); return;
            case "ArrowUp":
                // Focus auf Controls behalten (nicht blurren!)
                showControls(); e.preventDefault(); return;
            case "ArrowDown":
                // Focus auf Controls behalten (nicht blurren!)
                showControls(); e.preventDefault(); return;
            case "Enter":
                active.click(); showControls(); e.preventDefault(); return;
        }
    }

    // --- Standard Player-Tasten ---
    switch (key) {
        case " ": case "Play": case "Pause":
            togglePlay(); e.preventDefault(); break;
        case "Enter":
            if (!controlsVisible) {
                // Controls einblenden und Play-Button fokussieren
                showControls();
                if (playBtn) playBtn.focus();
            } else if (!buttonFocused) {
                // Controls sichtbar aber kein Button fokussiert -> Focus auf Play-Button
                // (NICHT togglePlay - sonst kommt man nie ins Menue!)
                if (playBtn) playBtn.focus();
            } else {
                togglePlay();
            }
            e.preventDefault(); break;
        case "ArrowLeft": case "Rewind":
            seekRelative(-10); showControls(); e.preventDefault(); break;
        case "ArrowRight": case "FastForward":
            seekRelative(10); showControls(); e.preventDefault(); break;
        case "ArrowUp":
            if (!controlsVisible) {
                showControls();
                if (playBtn) playBtn.focus();
            } else if (!buttonFocused) {
                // Kein Button fokussiert -> Play-Button fokussieren
                if (playBtn) playBtn.focus();
            }
            showControls(); e.preventDefault(); break;
        case "ArrowDown":
            if (!controlsVisible) {
                showControls();
                if (playBtn) playBtn.focus();
            } else if (!buttonFocused) {
                if (playBtn) playBtn.focus();
            }
            showControls(); e.preventDefault(); break;
        case "Escape": case "Backspace": case "Stop":
            saveProgress();
            if (useNativePlayer) _cleanupNativePlayer();
            if (useDirectPlay) _cleanupDirectPlay();
            cleanupHLS();
            goBack();
            e.preventDefault(); break;
        case "f":
            toggleFullscreen(); e.preventDefault(); break;
        case "s":
            togglePopup(); e.preventDefault(); break;
        case "n":
            if (cfg.nextVideoId) playNextEpisode();
            e.preventDefault(); break;
        // Samsung Farbtasten: Direkt-Zugriff auf Popup-Sektionen
        case "ColorRed":
            openPopupSection("audio"); e.preventDefault(); break;
        case "ColorGreen":
            openPopupSection("subs"); e.preventDefault(); break;
        case "ColorYellow":
            openPopupSection("quality"); e.preventDefault(); break;
        case "ColorBlue":
            toggleDebugInfo(); e.preventDefault(); break;
        case "i":
            toggleDebugInfo(); e.preventDefault(); break;
    }
}

// === Watch-Progress speichern ===

function saveProgress(completed) {
    if (!cfg.videoId || (!videoEl && !useDirectPlay && !useNativePlayer)) return;
    const dur = getDuration();
    // Bei completed: Position = Duration (garantiert ueber Schwelle)
    const pos = completed ? dur : getCurrentTime();
    if (pos < 5 && !completed) return;

    const payload = {
        video_id: cfg.videoId,
        position_sec: pos,
        duration_sec: dur,
    };
    if (completed) payload.completed = true;

    return fetch("/tv/api/watch-progress", {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify(payload),
    }).catch(() => {});
}

window.addEventListener("beforeunload", () => {
    // Fortschritt per sendBeacon speichern (zuverlaessig beim Seitenabbau)
    if (cfg.videoId && (videoEl || useDirectPlay || useNativePlayer)) {
        const dur = getDuration();
        const pos = getCurrentTime();
        if (pos >= 5) {
            navigator.sendBeacon("/tv/api/watch-progress",
                new Blob([JSON.stringify({
                    video_id: cfg.videoId,
                    position_sec: pos,
                    duration_sec: dur,
                })], {type: "application/json"}));
        }
    }
    // VKNative bereinigen
    if (useNativePlayer && window.VKNative) {
        try { window.VKNative.stop(); } catch (e) { /* ignorieren */ }
    }
    // Legacy AVPlay bereinigen
    if (useDirectPlay && typeof AVPlayBridge !== "undefined") {
        AVPlayBridge.stop();
    }
    // HLS-Session per sendBeacon beenden (fetch wird beim Seitenabbau abgebrochen)
    if (hlsSessionId) {
        navigator.sendBeacon(`/tv/api/hls/${hlsSessionId}/stop`, "");
    }
    if (hlsInstance) {
        hlsInstance.destroy();
        hlsInstance = null;
    }
});

// === Button-Status aktualisieren ===

function updatePlayerButtons() {
    // CC-Button: aktiv wenn Untertitel an
    var btnSubs = document.getElementById("btn-subs");
    if (btnSubs) btnSubs.classList.toggle("active", currentSub >= 0);
    // Quality-Badge: aktuellen Modus anzeigen
    var badge = document.getElementById("quality-badge");
    if (badge) {
        var labels = {uhd: "4K", hd: "HD", sd: "SD", low: "LD"};
        badge.textContent = labels[currentQuality] || "HD";
    }
    // Audio-Button: aktuelle Sprache anzeigen (Tooltip)
    var btnAudio = document.getElementById("btn-audio");
    if (btnAudio && videoInfo && videoInfo.audio_tracks && videoInfo.audio_tracks[currentAudio]) {
        var lang = videoInfo.audio_tracks[currentAudio].lang;
        btnAudio.title = langName(lang) || "Audio";
    }
}

// === Hilfsfunktionen ===

const LANG_NAMES = {
    deu: "Deutsch", eng: "English", fra: "Fran\u00e7ais",
    spa: "Espa\u00f1ol", ita: "Italiano", jpn: "\u65e5\u672c\u8a9e",
    kor: "\ud55c\uad6d\uc5b4", por: "Portugu\u00eas",
    rus: "\u0420\u0443\u0441\u0441\u043a\u0438\u0439",
    zho: "\u4e2d\u6587", und: "Unbekannt",
};

function langName(code) {
    return LANG_NAMES[code] || code || "";
}

// === Debug-Info-Overlay ===

let debugVisible = false;
let debugUpdateId = null;

/** Debug-Info ein-/ausblenden (Toggle mit "i"-Taste oder Blau-Taste auf Fernbedienung) */
function toggleDebugInfo() {
    debugVisible = !debugVisible;
    var el = document.getElementById("player-debug");
    if (!el) return;

    if (debugVisible) {
        el.style.display = "";
        _updateDebugInfo();
        // Alle 500ms aktualisieren
        debugUpdateId = setInterval(_updateDebugInfo, 500);
    } else {
        el.style.display = "none";
        if (debugUpdateId) { clearInterval(debugUpdateId); debugUpdateId = null; }
    }
}

function _updateDebugInfo() {
    var el = document.getElementById("player-debug");
    if (!el || !debugVisible) return;

    var lines = [];

    // Decoder / Wiedergabe-Modus
    var decoder = "Unbekannt";
    var decoderClass = "dbg-val";
    if (useNativePlayer && window.VKNative) {
        decoder = "VKNative AVPlay (Direct-Play)";
        decoderClass = "dbg-ok";
    } else if (useDirectPlay && typeof AVPlayBridge !== "undefined") {
        decoder = "AVPlay Legacy (Direct-Play)";
        decoderClass = "dbg-ok";
    } else if (hlsInstance) {
        decoder = "HLS (hls.js)";
        decoderClass = "dbg-val";
    } else if (hlsSessionId) {
        decoder = "HLS (nativ)";
        decoderClass = "dbg-val";
    } else if (videoEl && videoEl.src) {
        if (videoEl.src.indexOf("/stream?") >= 0) {
            decoder = "Legacy Pipe-Streaming";
            decoderClass = "dbg-warn";
        } else {
            decoder = "Browser Direct-Play";
            decoderClass = "dbg-ok";
        }
    }
    lines.push(_dbgRow("Decoder", decoder, decoderClass));

    // Plattform
    var platform = "Browser";
    if (window.VKNative) platform = "VKNative (" + window.VKNative.platform + " v" + (window.VKNative.version || "?") + ")";
    else if (isTizenTV()) platform = "Tizen";
    lines.push(_dbgRow("Plattform", platform));

    lines.push('<hr class="dbg-sep">');

    // Video-Codec + Aufloesung
    if (videoInfo) {
        var vc = videoInfo.video_codec_normalized || videoInfo.video_codec || "?";
        var res = "";
        if (videoInfo.width && videoInfo.height) res = videoInfo.width + "x" + videoInfo.height;
        var container = videoInfo.container || "?";
        lines.push(_dbgRow("Video", vc.toUpperCase() + (res ? " " + res : ""), "dbg-ok"));
        lines.push(_dbgRow("Container", container));

        // Audio-Tracks
        if (videoInfo.audio_tracks && videoInfo.audio_tracks.length) {
            var at = videoInfo.audio_tracks[currentAudio];
            if (at) {
                var aCodec = (at.codec || "?").toUpperCase();
                var aCh = at.channels ? at.channels + "ch" : "";
                var aLang = langName(at.lang);
                lines.push(_dbgRow("Audio", aCodec + " " + aCh + " " + aLang));
            }
            lines.push(_dbgRow("Audio-Spuren", videoInfo.audio_tracks.length + ""));
        }
        // Audio-Codecs (Quell-Datei)
        if (videoInfo.audio_codecs && videoInfo.audio_codecs.length) {
            lines.push(_dbgRow("Quell-Audio", videoInfo.audio_codecs.join(", ")));
        }
    }

    lines.push('<hr class="dbg-sep">');

    // Position / Dauer
    var curSec = getCurrentTime();
    var durSec = getDuration();
    lines.push(_dbgRow("Position", formatTime(curSec) + " / " + formatTime(durSec)));

    // Qualitaet + Geschwindigkeit
    var qualLabels = {uhd: "Ultra HD", hd: "HD", sd: "SD", low: "Niedrig"};
    lines.push(_dbgRow("Qualitaet", qualLabels[currentQuality] || currentQuality));
    if (currentSpeed !== 1.0) lines.push(_dbgRow("Speed", currentSpeed + "x"));

    // HLS-spezifische Infos
    if (hlsInstance) {
        lines.push('<hr class="dbg-sep">');
        lines.push(_dbgRow("HLS Session", hlsSessionId || "-"));

        // Buffer-Level
        try {
            var buffered = videoEl.buffered;
            if (buffered && buffered.length > 0) {
                var bufEnd = buffered.end(buffered.length - 1);
                var bufAhead = Math.max(0, bufEnd - (videoEl.currentTime || 0));
                var bufClass = bufAhead > 10 ? "dbg-ok" : bufAhead > 3 ? "dbg-val" : "dbg-warn";
                lines.push(_dbgRow("Puffer", bufAhead.toFixed(1) + "s voraus", bufClass));
            }
        } catch (e) {}

        // hls.js interne Stats
        if (hlsInstance.streamController) {
            try {
                var levels = hlsInstance.levels;
                var curLevel = hlsInstance.currentLevel;
                if (levels && levels[curLevel]) {
                    var lv = levels[curLevel];
                    if (lv.bitrate) lines.push(_dbgRow("Bitrate", _formatBitrate(lv.bitrate)));
                    if (lv.width && lv.height) lines.push(_dbgRow("HLS-Res.", lv.width + "x" + lv.height));
                }
            } catch (e) {}
        }

        if (hlsSeekOffset > 0) {
            lines.push(_dbgRow("Seek-Offset", formatTime(hlsSeekOffset)));
        }
    }

    // Browser Direct-Play: Buffer-Info
    if (!hlsInstance && videoEl && videoEl.buffered && videoEl.buffered.length > 0) {
        try {
            var bufEnd2 = videoEl.buffered.end(videoEl.buffered.length - 1);
            var bufAhead2 = Math.max(0, bufEnd2 - (videoEl.currentTime || 0));
            if (bufAhead2 > 0) {
                var bufClass2 = bufAhead2 > 10 ? "dbg-ok" : bufAhead2 > 3 ? "dbg-val" : "dbg-warn";
                lines.push(_dbgRow("Puffer", bufAhead2.toFixed(1) + "s voraus", bufClass2));
            }
        } catch (e) {}
    }

    // AVPlay-Stats (Tizen: Stream-Info vom Parent-Frame)
    if (useNativePlayer && window.VKNative && window.VKNative.requestStats) {
        window.VKNative.requestStats(); // Async Abfrage, Ergebnis in _lastStats
        var avStats = window.VKNative._lastStats;
        if (avStats) {
            lines.push('<hr class="dbg-sep">');
            var stateClass = avStats.state === "PLAYING" ? "dbg-ok" :
                avStats.state === "PAUSED" ? "dbg-val" : "dbg-warn";
            lines.push(_dbgRow("AVPlay-Status", avStats.state || "?", stateClass));
            if (avStats.streams && avStats.streams.length) {
                for (var si = 0; si < avStats.streams.length; si++) {
                    var stream = avStats.streams[si];
                    var sType = stream.type || "?";
                    var sExtra = "";
                    if (stream.extra) {
                        try {
                            // extra_info ist JSON-String mit Codec-Details
                            var ex = typeof stream.extra === "string" ? JSON.parse(stream.extra) : stream.extra;
                            if (ex.fourCC) sExtra += ex.fourCC;
                            else if (ex.mimeType) sExtra += ex.mimeType;
                            if (ex.Width && ex.Height) sExtra += " " + ex.Width + "x" + ex.Height;
                            if (ex.Bit_rate) sExtra += " " + _formatBitrate(parseInt(ex.Bit_rate));
                            if (ex.channels) sExtra += " " + ex.channels + "ch";
                            if (ex.sample_rate) sExtra += " " + (parseInt(ex.sample_rate) / 1000).toFixed(1) + "kHz";
                        } catch (e) {
                            sExtra = String(stream.extra).substring(0, 60);
                        }
                    }
                    var sLabel = sType === "VIDEO" ? "AVP-Video" : sType === "AUDIO" ? "AVP-Audio" : "AVP-" + sType;
                    lines.push(_dbgRow(sLabel, sExtra || "-"));
                }
            }
        }
    }

    // Client-Codecs
    if (clientCodecs && clientCodecs.length) {
        lines.push('<hr class="dbg-sep">');
        lines.push(_dbgRow("Client-Codecs", clientCodecs.join(", ")));
    }

    // Video-Element Stats (Dropped Frames etc.)
    if (videoEl && videoEl.getVideoPlaybackQuality) {
        try {
            var q = videoEl.getVideoPlaybackQuality();
            if (q.totalVideoFrames > 0) {
                var dropped = q.droppedVideoFrames;
                var total = q.totalVideoFrames;
                var dropClass = dropped === 0 ? "dbg-ok" : dropped < 10 ? "dbg-val" : "dbg-warn";
                lines.push(_dbgRow("Frames", total + " total, " + dropped + " dropped", dropClass));
            }
        } catch (e) {}
    }

    // Tasten-Hilfe
    var helpText = window.VKNative ? "Blau = Debug" : "i = schliessen";
    el.innerHTML = "<b>Debug-Info</b> <small>(" + helpText + ")</small><hr class='dbg-sep'>" + lines.join("");
}

function _dbgRow(label, value, cls) {
    cls = cls || "dbg-val";
    return '<div><span class="dbg-label">' + label + '</span> <span class="' + cls + '">' + value + '</span></div>';
}

function _formatBitrate(bps) {
    if (bps >= 1000000) return (bps / 1000000).toFixed(1) + " Mbit/s";
    if (bps >= 1000) return (bps / 1000).toFixed(0) + " kbit/s";
    return bps + " bit/s";
}
