"""
_camera_state.py — Pure-Python camera health state (no ROS2 deps).
Importable without rclpy for unit testing.
"""

from __future__ import annotations

import time
from collections import deque
from dataclasses import dataclass, field
from typing import Optional


@dataclass
class CameraState:
    topic:     str
    v4l2_dev:  Optional[str]         # None for D435i (USB — no v4l2 reset)

    last_frame_t:  Optional[float] = None  # monotonic seconds
    total_frames:  int             = 0
    _ts_window:    deque           = field(default_factory=lambda: deque(maxlen=200))
    _last_reset_t: float           = 0.0   # for reset cooldown

    def on_frame(self) -> None:
        now = time.monotonic()
        self.last_frame_t = now
        self.total_frames += 1
        self._ts_window.append(now)

    @property
    def age_s(self) -> float:
        if self.last_frame_t is None:
            return float('inf')
        return time.monotonic() - self.last_frame_t

    def fps(self, window_s: float = 5.0) -> float:
        now = time.monotonic()
        recent = [t for t in self._ts_window if now - t <= window_s]
        if len(recent) < 2:
            return 0.0
        return (len(recent) - 1) / (recent[-1] - recent[0])

    def status(self, warn_thr: float, error_thr: float) -> str:
        age = self.age_s
        if age == float('inf'):
            return 'never'
        if age > error_thr:
            return 'error'
        if age > warn_thr:
            return 'warn'
        return 'ok'

    def should_reset(self, cooldown_s: float = 30.0) -> bool:
        return (
            self.v4l2_dev is not None
            and time.monotonic() - self._last_reset_t > cooldown_s
        )

    def mark_reset(self) -> None:
        self._last_reset_t = time.monotonic()
