"""VideoPlayer — plays a local video file and delivers frames via frame_ready signal.

The public interface mirrors CameraService so MainWindow can treat both
interchangeably: both emit frame_ready(QVideoFrame).
"""

from __future__ import annotations

import logging
from pathlib import Path

from PySide6.QtCore import QObject, QUrl, Signal, Slot
from PySide6.QtMultimedia import (
    QMediaPlayer,
    QVideoFrame,
    QVideoSink,
)

logger = logging.getLogger(__name__)


class VideoPlayer(QObject):
    """
    Wraps QMediaPlayer + QVideoSink to replay a local video file.

    Signal flow (identical interface to CameraService):
        VideoPlayer.frame_ready(QVideoFrame) → FrameDispatcher

    Notes:
        - Playback is real-time (1×) — no seek/pause in this version.
        - At end-of-file: emits playback_stopped() and stops.
        - On any error: emits playback_error(str) then playback_stopped().
    """

    frame_ready = Signal(QVideoFrame)
    playback_started = Signal()
    playback_stopped = Signal()
    playback_error = Signal(str)

    def __init__(self, parent: QObject | None = None) -> None:
        super().__init__(parent)

        self._player = QMediaPlayer(self)
        self._sink = QVideoSink(self)

        self._player.setVideoSink(self._sink)

        self._sink.videoFrameChanged.connect(self._on_frame)
        self._player.playbackStateChanged.connect(self._on_playback_state_changed)
        self._player.errorOccurred.connect(self._on_error)

        self._current_path: str | None = None

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------

    def play(self, path: str) -> None:
        """Open and start playing a video file."""
        self.stop()
        p = Path(path)
        if not p.exists():
            msg = f"Video file not found: {path}"
            logger.error(msg)
            self.playback_error.emit(msg)
            return

        self._current_path = path
        url = QUrl.fromLocalFile(str(p.resolve()))
        self._player.setSource(url)
        self._player.play()
        logger.info("VideoPlayer: starting playback of '%s'", p.name)

    def stop(self) -> None:
        """Stop playback and clear source."""
        if self._player.playbackState() != QMediaPlayer.PlaybackState.StoppedState:
            self._player.stop()
        self._player.setSource(QUrl())
        self._current_path = None

    @property
    def is_playing(self) -> bool:
        return (
            self._player.playbackState()
            == QMediaPlayer.PlaybackState.PlayingState
        )

    @property
    def current_path(self) -> str | None:
        return self._current_path

    # ------------------------------------------------------------------
    # Private slots
    # ------------------------------------------------------------------

    @Slot(QVideoFrame)
    def _on_frame(self, frame: QVideoFrame) -> None:
        if frame.isValid():
            self.frame_ready.emit(frame)

    @Slot(QMediaPlayer.PlaybackState)
    def _on_playback_state_changed(self, state: QMediaPlayer.PlaybackState) -> None:
        if state == QMediaPlayer.PlaybackState.PlayingState:
            logger.info("VideoPlayer: playing")
            self.playback_started.emit()
        elif state == QMediaPlayer.PlaybackState.StoppedState:
            logger.info("VideoPlayer: stopped")
            self.playback_stopped.emit()

    @Slot(QMediaPlayer.Error, str)
    def _on_error(self, error: QMediaPlayer.Error, error_string: str) -> None:
        if error == QMediaPlayer.Error.NoError:
            return
        msg = f"VideoPlayer error: {error_string}"
        logger.error(msg)
        self.playback_error.emit(msg)
        self.playback_stopped.emit()
