import { createTranslator, PlayerController } from '@livepeer-frameworks/player-core';

const initialState = {
    state: "booting",
    streamState: null,
    endpoints: null,
    metadata: null,
    videoElement: null,
    currentTime: 0,
    duration: NaN,
    isPlaying: false,
    isPaused: true,
    isBuffering: false,
    isMuted: false,
    volume: 1,
    error: null,
    errorDetails: null,
    isPassiveError: false,
    hasPlaybackStarted: false,
    isHoldingSpeed: false,
    holdSpeed: 2,
    isHovering: false,
    shouldShowControls: false,
    isLoopEnabled: false,
    isFullscreen: false,
    isPiPActive: false,
    isEffectivelyLive: false,
    shouldShowIdleScreen: true,
    currentPlayerInfo: null,
    currentSourceInfo: null,
    playbackQuality: null,
    subtitlesEnabled: false,
    qualities: [],
    textTracks: [],
    streamInfo: null,
    toast: null,
    thumbnailCues: [],
    loadingPoster: null,
    shouldShowLoadingPoster: false,
};
class PlayerControllerHost {
    constructor(host) {
        this.controller = null;
        this.unsubs = [];
        this.currentConfig = null;
        this.s = { ...initialState };
        /** Translation function, updated when locale changes. */
        this.t = createTranslator({ locale: "en" });
        this.host = host;
        host.addController(this);
    }
    /** Rebuild the translator when locale or custom translations change. */
    updateTranslator(config) {
        this.t = createTranslator(config);
        this.host.requestUpdate();
    }
    // ---- Configuration & Lifecycle ----
    configure(config) {
        this.currentConfig = config;
    }
    async attach(container) {
        if (!this.currentConfig)
            return;
        this.teardown();
        const controller = new PlayerController({
            contentId: this.currentConfig.contentId,
            contentType: this.currentConfig.contentType,
            endpoints: this.currentConfig.endpoints,
            gatewayUrl: this.currentConfig.gatewayUrl,
            mistUrl: this.currentConfig.mistUrl,
            authToken: this.currentConfig.authToken,
            playbackAuth: this.currentConfig.playbackAuth,
            locale: this.currentConfig.locale,
            translations: this.currentConfig.translations,
            autoplay: this.currentConfig.autoplay,
            muted: this.currentConfig.muted,
            controls: this.currentConfig.controls,
            poster: this.currentConfig.poster,
            animatePreroll: this.currentConfig.animatePreroll,
            debug: this.currentConfig.debug,
            forcePlayer: this.currentConfig.forcePlayer,
            forceType: this.currentConfig.forceType,
            forceSource: this.currentConfig.forceSource,
            playbackMode: this.currentConfig.playbackMode,
        });
        this.controller = controller;
        this.subscribeToEvents(controller);
        const initialPoster = controller.getLoadingPoster?.();
        this.update({
            isLoopEnabled: controller.isLoopEnabled(),
            shouldShowIdleScreen: controller.shouldShowIdleScreen?.() ?? this.s.shouldShowIdleScreen,
            loadingPoster: initialPoster ?? this.s.loadingPoster,
            shouldShowLoadingPoster: controller.getShouldShowLoadingPoster?.() ?? this.s.shouldShowLoadingPoster,
        });
        try {
            await controller.attach(container);
        }
        catch (err) {
            console.warn("[PlayerControllerHost] Attach failed:", err);
        }
    }
    hostConnected() {
        // Controller attachment happens in firstUpdated of the host element
    }
    hostDisconnected() {
        this.teardown();
        this.s = { ...initialState };
    }
    teardown() {
        this.unsubs.forEach((fn) => fn());
        this.unsubs = [];
        this.controller?.destroy();
        this.controller = null;
    }
    // ---- State Updates ----
    update(partial) {
        Object.assign(this.s, partial);
        this.host.requestUpdate();
    }
    syncState() {
        if (!this.controller)
            return;
        const c = this.controller;
        this.update({
            isPlaying: c.isPlaying(),
            isPaused: c.isPaused(),
            isBuffering: c.isBuffering(),
            isMuted: c.isMuted(),
            volume: c.getVolume(),
            hasPlaybackStarted: c.hasPlaybackStarted(),
            shouldShowControls: c.shouldShowControls(),
            shouldShowIdleScreen: c.shouldShowIdleScreen(),
            shouldShowLoadingPoster: c.getShouldShowLoadingPoster(),
            playbackQuality: c.getPlaybackQuality(),
            isLoopEnabled: c.isLoopEnabled(),
            subtitlesEnabled: c.isSubtitlesEnabled(),
            qualities: c.getQualities(),
            textTracks: c.getTextTracks(),
            streamInfo: c.getStreamInfo(),
            thumbnailCues: c.getThumbnailCues?.() ?? this.s.thumbnailCues,
            loadingPoster: c.getLoadingPoster?.() ?? this.s.loadingPoster,
        });
    }
    // ---- Event Subscriptions (mirrors usePlayerController exactly) ----
    subscribeToEvents(controller) {
        const u = this.unsubs;
        u.push(controller.on("stateChange", ({ state }) => {
            this.update({
                state,
                shouldShowIdleScreen: controller.shouldShowIdleScreen(),
                thumbnailCues: controller.getThumbnailCues?.() ?? this.s.thumbnailCues,
                loadingPoster: controller.getLoadingPoster?.() ?? this.s.loadingPoster,
            });
            this.dispatchEvent("fw-state-change", { state });
        }));
        u.push(controller.on("streamStateChange", ({ state: streamState }) => {
            this.update({
                streamState,
                metadata: controller.getMetadata(),
                isEffectivelyLive: controller.isEffectivelyLive(),
                shouldShowIdleScreen: controller.shouldShowIdleScreen(),
                thumbnailCues: controller.getThumbnailCues?.() ?? this.s.thumbnailCues,
                loadingPoster: controller.getLoadingPoster?.() ?? this.s.loadingPoster,
            });
            this.dispatchEvent("fw-stream-state", { state: streamState });
        }));
        u.push(controller.on("timeUpdate", ({ currentTime, duration }) => {
            const next = {
                currentTime,
                duration,
                shouldShowControls: controller.shouldShowControls(),
                shouldShowLoadingPoster: controller.getShouldShowLoadingPoster(),
                thumbnailCues: controller.getThumbnailCues?.() ?? this.s.thumbnailCues,
                loadingPoster: controller.getLoadingPoster?.() ?? this.s.loadingPoster,
            };
            if (this.s.qualities.length === 0) {
                const qualities = controller.getQualities();
                if (qualities.length > 0) {
                    next.qualities = qualities;
                }
            }
            this.update(next);
            this.dispatchEvent("fw-time-update", { currentTime, duration });
        }));
        u.push(controller.on("error", ({ error }) => {
            this.update({
                error,
                isPassiveError: controller.isPassiveError(),
                shouldShowIdleScreen: controller.shouldShowIdleScreen(),
            });
            this.dispatchEvent("fw-error", { error });
        }));
        u.push(controller.on("errorCleared", () => {
            this.update({ error: null, isPassiveError: false });
        }));
        u.push(controller.on("ready", ({ videoElement }) => {
            this.update({
                videoElement,
                endpoints: controller.getEndpoints(),
                metadata: controller.getMetadata(),
                streamInfo: controller.getStreamInfo(),
                isEffectivelyLive: controller.isEffectivelyLive(),
                shouldShowIdleScreen: controller.shouldShowIdleScreen(),
                currentPlayerInfo: controller.getCurrentPlayerInfo(),
                currentSourceInfo: controller.getCurrentSourceInfo(),
                qualities: controller.getQualities(),
                textTracks: controller.getTextTracks(),
                thumbnailCues: controller.getThumbnailCues?.() ?? this.s.thumbnailCues,
                loadingPoster: controller.getLoadingPoster?.() ?? this.s.loadingPoster,
                shouldShowLoadingPoster: controller.getShouldShowLoadingPoster(),
            });
            this.dispatchEvent("fw-ready", { videoElement });
            this.syncState();
            const handleVideoEvent = () => {
                if (this.controller?.shouldSuppressVideoEvents?.())
                    return;
                this.syncState();
            };
            videoElement.addEventListener("play", handleVideoEvent);
            videoElement.addEventListener("pause", handleVideoEvent);
            videoElement.addEventListener("waiting", handleVideoEvent);
            videoElement.addEventListener("playing", handleVideoEvent);
            u.push(() => {
                videoElement.removeEventListener("play", handleVideoEvent);
                videoElement.removeEventListener("pause", handleVideoEvent);
                videoElement.removeEventListener("waiting", handleVideoEvent);
                videoElement.removeEventListener("playing", handleVideoEvent);
            });
        }));
        u.push(controller.on("playerSelected", ({ player: _player, source }) => {
            this.update({
                currentPlayerInfo: controller.getCurrentPlayerInfo(),
                currentSourceInfo: { url: source.url, type: source.type },
                playbackQuality: null,
                qualities: controller.getQualities(),
                textTracks: controller.getTextTracks(),
            });
            this.syncState();
        }));
        u.push(controller.on("volumeChange", ({ volume, muted }) => {
            this.update({ volume, isMuted: muted });
            this.dispatchEvent("fw-volume-change", { volume, muted });
        }));
        u.push(controller.on("loopChange", ({ isLoopEnabled }) => {
            this.update({ isLoopEnabled });
        }));
        u.push(controller.on("fullscreenChange", ({ isFullscreen }) => {
            this.update({ isFullscreen });
            this.dispatchEvent("fw-fullscreen-change", { isFullscreen });
        }));
        u.push(controller.on("pipChange", ({ isPiP }) => {
            this.update({ isPiPActive: isPiP });
            this.dispatchEvent("fw-pip-change", { isPiP });
        }));
        u.push(controller.on("holdSpeedStart", ({ speed }) => {
            this.update({ isHoldingSpeed: true, holdSpeed: speed });
        }));
        u.push(controller.on("holdSpeedEnd", () => {
            this.update({ isHoldingSpeed: false });
        }));
        u.push(controller.on("hoverStart", () => {
            this.update({ isHovering: true, shouldShowControls: true });
        }));
        u.push(controller.on("hoverEnd", () => {
            this.update({
                isHovering: false,
                shouldShowControls: controller.shouldShowControls(),
            });
        }));
        u.push(controller.on("captionsChange", ({ enabled }) => {
            this.update({ subtitlesEnabled: enabled, textTracks: controller.getTextTracks() });
        }));
        u.push(controller.on("protocolSwapped", (data) => {
            const message = `Switched to ${data.toProtocol}`;
            this.update({ toast: { message, timestamp: Date.now() } });
            this.dispatchEvent("fw-protocol-swapped", data);
        }));
        u.push(controller.on("playbackFailed", (data) => {
            this.update({
                error: data.message,
                errorDetails: data.details ?? null,
                isPassiveError: false,
            });
            this.dispatchEvent("fw-playback-failed", {
                code: data.code,
                message: data.message,
            });
        }));
        u.push(controller.on("thumbnailCuesChange", ({ cues }) => {
            this.update({
                thumbnailCues: cues,
                loadingPoster: controller.getLoadingPoster?.() ?? this.s.loadingPoster,
                shouldShowLoadingPoster: controller.getShouldShowLoadingPoster(),
            });
        }));
        u.push(controller.on("loadingPosterChange", ({ poster }) => {
            this.update({
                loadingPoster: poster,
                shouldShowLoadingPoster: controller.getShouldShowLoadingPoster(),
            });
        }));
        // shouldShowLoadingPoster derives from inputs that fire across multiple
        // events. Recompute on every relevant transition; cheap & idempotent.
        const recomputeShouldShow = () => {
            const next = controller.getShouldShowLoadingPoster();
            if (next !== this.s.shouldShowLoadingPoster) {
                this.update({ shouldShowLoadingPoster: next });
            }
        };
        for (const evt of [
            "stateChange",
            "streamStateChange",
            "timeUpdate",
            "error",
            "errorCleared",
            "ready",
        ]) {
            u.push(controller.on(evt, recomputeShouldShow));
        }
    }
    // ---- Event Dispatching ----
    dispatchEvent(name, detail) {
        this.host.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }));
    }
    // ---- Action Methods ----
    async play() {
        await this.controller?.play();
    }
    pause() {
        this.controller?.pause();
    }
    togglePlay() {
        this.controller?.togglePlay();
    }
    seek(time) {
        this.controller?.seek(time);
    }
    seekBy(delta) {
        this.controller?.seekBy(delta);
    }
    jumpToLive() {
        this.controller?.jumpToLive();
    }
    setVolume(volume) {
        this.controller?.setVolume(volume);
    }
    toggleMute() {
        this.controller?.toggleMute();
    }
    toggleLoop() {
        this.controller?.toggleLoop();
    }
    async toggleFullscreen() {
        await this.controller?.toggleFullscreen();
    }
    async togglePiP() {
        await this.controller?.togglePictureInPicture();
    }
    toggleSubtitles() {
        this.controller?.toggleSubtitles();
    }
    clearError() {
        this.controller?.clearError();
        this.update({ error: null, errorDetails: null, isPassiveError: false });
    }
    dismissToast() {
        this.update({ toast: null });
    }
    async retry() {
        await this.controller?.retry();
    }
    async tryNextSource() {
        await this.controller?.retryWithFallback();
    }
    canAttemptFallback() {
        return this.controller?.canAttemptFallback() ?? false;
    }
    async reload() {
        await this.controller?.reload();
    }
    getQualities() {
        return this.controller?.getQualities() ?? [];
    }
    selectQuality(id) {
        this.controller?.selectQuality(id);
    }
    getTextTracks() {
        return this.controller?.getTextTracks() ?? [];
    }
    selectTextTrack(id) {
        this.controller?.selectTextTrack(id);
    }
    setPlaybackRate(rate) {
        this.controller?.setPlaybackRate(rate);
    }
    getSeekableStart() {
        return this.controller?.getSeekableStart() ?? 0;
    }
    getLiveEdge() {
        return this.controller?.getLiveEdge() ?? 0;
    }
    canSeekStream() {
        return this.controller?.canSeekStream() ?? false;
    }
    getBufferedRanges() {
        return this.controller?.getBufferedRanges() ?? null;
    }
    async getStats() {
        return this.controller?.getStats();
    }
    handleMouseEnter() {
        this.controller?.handleMouseEnter();
        this.update({ isHovering: true, shouldShowControls: true });
    }
    handleMouseLeave() {
        this.controller?.handleMouseLeave();
        this.update({
            isHovering: false,
            shouldShowControls: this.controller?.shouldShowControls() ?? false,
        });
    }
    handleMouseMove() {
        this.controller?.handleMouseMove();
        if (this.controller) {
            this.update({ shouldShowControls: this.controller.shouldShowControls() });
        }
    }
    handleTouchStart() {
        this.controller?.handleTouchStart();
        this.update({ shouldShowControls: true });
    }
    async setDevModeOptions(options) {
        await this.controller?.setDevModeOptions(options);
    }
    getController() {
        return this.controller;
    }
}

export { PlayerControllerHost };
//# sourceMappingURL=player-controller-host.js.map
