"""Stream lifecycle state machine: IDLE → STREAMING → COOLDOWN.

Manages on-demand scrcpy + WebRTC streaming. The stream only starts when
a viewer sends an offer (via MQTT) and stops after a cooldown period
when the last viewer disconnects.
"""

from __future__ import annotations

import asyncio
import logging
from enum import Enum
from typing import Callable, Awaitable

logger = logging.getLogger(__name__)


class StreamState(str, Enum):
    IDLE = "idle"
    STREAMING = "streaming"
    COOLDOWN = "cooldown"


class StreamLifecycle:
    """On-demand stream lifecycle with reference counting and cooldown.

    State transitions::

        IDLE ──offer arrives──→ STREAMING ──last viewer disconnects──→ COOLDOWN
          ▲                        ▲   │                                │
          │                        │   └──new offer (cancel timer)──────┘
          └───────timeout(30s)─────┘
               stop scrcpy, release resources
    """

    def __init__(
        self,
        cooldown_seconds: float = 30.0,
        on_start_stream: Callable[[], Awaitable[None]] | None = None,
        on_stop_stream: Callable[[], Awaitable[None]] | None = None,
    ) -> None:
        self._cooldown_seconds = cooldown_seconds
        self._on_start_stream = on_start_stream
        self._on_stop_stream = on_stop_stream
        self._state = StreamState.IDLE
        self._viewer_count = 0
        self._cooldown_task: asyncio.Task[None] | None = None

    @property
    def state(self) -> StreamState:
        return self._state

    @property
    def viewer_count(self) -> int:
        return self._viewer_count

    async def viewer_connected(self) -> None:
        """Called when a new viewer (WebRTC peer) connects."""
        self._viewer_count += 1
        logger.info("Viewer connected (count=%d, state=%s)", self._viewer_count, self._state)

        if self._state == StreamState.COOLDOWN:
            self._cancel_cooldown()
            self._state = StreamState.STREAMING
            logger.info("Cooldown cancelled, back to STREAMING")

        elif self._state == StreamState.IDLE:
            self._state = StreamState.STREAMING
            logger.info("Transitioning IDLE → STREAMING")
            if self._on_start_stream:
                await self._on_start_stream()

    async def viewer_disconnected(self) -> None:
        """Called when a viewer (WebRTC peer) disconnects."""
        self._viewer_count = max(0, self._viewer_count - 1)
        logger.info("Viewer disconnected (count=%d, state=%s)", self._viewer_count, self._state)

        if self._viewer_count == 0 and self._state == StreamState.STREAMING:
            self._state = StreamState.COOLDOWN
            logger.info("Last viewer gone, entering COOLDOWN (%.0fs)", self._cooldown_seconds)
            self._cooldown_task = asyncio.create_task(self._cooldown_timer())

    async def _cooldown_timer(self) -> None:
        try:
            await asyncio.sleep(self._cooldown_seconds)
            if self._state == StreamState.COOLDOWN and self._viewer_count == 0:
                self._state = StreamState.IDLE
                logger.info("Cooldown expired → IDLE, stopping stream")
                if self._on_stop_stream:
                    await self._on_stop_stream()
        except asyncio.CancelledError:
            pass

    def _cancel_cooldown(self) -> None:
        if self._cooldown_task and not self._cooldown_task.done():
            self._cooldown_task.cancel()
            self._cooldown_task = None

    async def force_stop(self) -> None:
        """Force-stop the stream immediately."""
        self._cancel_cooldown()
        if self._state != StreamState.IDLE:
            self._state = StreamState.IDLE
            self._viewer_count = 0
            if self._on_stop_stream:
                await self._on_stop_stream()
