"""Gestion caméra V4L2 pour Raspberry Pi."""

import sys, os

for p in ("/usr/lib/python3/dist-packages", "/usr/local/lib/python3/dist-packages"):
    if os.path.isdir(p) and p not in sys.path:
        sys.path.append(p)

# Forcer l'usage de Picamera2 (pas de fallback V4L2 qui lit du RAW unicam)
FORCE_PICAMERA2 = True


import cv2
import numpy as np
import time
import logging
from typing import Optional, Tuple
from threading import Lock

logger = logging.getLogger(__name__)


class Camera:
    """Interface caméra V4L2 optimisée pour Pi Zero 2W."""

    def __init__(self, width: int, height: int, fps: int):
        self.width = width
        self.height = height
        self.fps = fps
        self.cap: Optional[cv2.VideoCapture] = None
        self._lock = Lock()
        self._last_frame_time = 0.0
        self._frame_count = 0
        self._is_opened = False

    def open(self) -> bool:
        """Ouvre la caméra (Picamera2 obligatoire sur Debian 12)."""
        try:
            self.close()
        except Exception:
            pass

        # Import tardif pour lever une erreur claire si Picamera2 est absent
        try:
            from picamera2 import Picamera2  # type: ignore
        except Exception as e:
            import logging

            logging.getLogger(__name__).error(
                "Picamera2 introuvable dans cet interpréteur Python. "
                "Installe-le via: sudo apt install -y python3-picamera2 ; "
                "ou recrée le venv avec --system-site-packages. Détail: %s",
                e,
            )
            return False

        try:
            self._picam2 = Picamera2()  # type: ignore
            frame_us = int(1_000_000 / max(1, self.fps))
            config = self._picam2.create_video_configuration(
                main={"size": (self.width, self.height), "format": "RGB888"},
                controls={"FrameDurationLimits": (frame_us, frame_us)},
                buffer_count=4,
            )
            self._picam2.configure(config)
            self._picam2.start()
            import time as _t

            _t.sleep(0.2)
            self._backend = "picamera2"
            self._is_opened = True
            logger.info(
                f"Caméra (Picamera2) ouverte: {self.width}x{self.height} @ {self.fps} FPS"
            )
            return True
        except Exception as e:
            logger.error(f"Erreur ouverture Picamera2: {e}")
            return False

    def read_frame(self) -> Optional[np.ndarray]:
        """Lit une frame RGB de la caméra."""
        if not self._is_opened or self.cap is None:
            return None

        with self._lock:
            try:
                ret, frame = self.cap.read()
                if not ret or frame is None:
                    logger.warning("Impossible de lire frame caméra")
                    return None

                # Conversion BGR -> RGB in-place
                frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

                # Stats timing
                current_time = time.time()
                if self._last_frame_time > 0:
                    delta = current_time - self._last_frame_time
                    if delta > 0:
                        actual_fps = 1.0 / delta
                        if self._frame_count % 30 == 0:  # Log tous les 30 frames
                            logger.debug(f"FPS capture: {actual_fps:.1f}")

                self._last_frame_time = current_time
                self._frame_count += 1

                return frame_rgb

            except Exception as e:
                logger.error(f"Erreur lecture frame: {e}")
                return None

    def close(self) -> None:
        """Ferme la caméra."""
        with self._lock:
            if self.cap is not None:
                self.cap.release()
                self.cap = None
            self._is_opened = False
        logger.info("Caméra fermée")

    @property
    def is_opened(self) -> bool:
        """Vérifie si la caméra est ouverte."""
        return self._is_opened and self.cap is not None and self.cap.isOpened()

    def get_fps_stats(self) -> Tuple[float, int]:
        """Retourne FPS moyen et nombre de frames."""
        if self._last_frame_time == 0 or self._frame_count == 0:
            return 0.0, 0

        elapsed = time.time() - (
            self._last_frame_time - (self._frame_count - 1) * (1.0 / self.fps)
        )
        avg_fps = self._frame_count / elapsed if elapsed > 0 else 0.0

        return avg_fps, self._frame_count


class MockCamera:
    """Caméra simulée pour tests."""

    def __init__(self, width: int, height: int, fps: int):
        self.width = width
        self.height = height
        self.fps = fps
        self._is_opened = False
        self._frame_count = 0

    def open(self) -> bool:
        """Simule ouverture caméra."""
        self._is_opened = True
        logger.info(f"Mock caméra ouverte: {self.width}x{self.height} @ {self.fps} FPS")
        return True

    def read_frame(self) -> Optional[np.ndarray]:
        """Génère frame synthétique RGB."""
        if not self._is_opened:
            return None

        # Frame synthétique avec gradient
        frame = np.zeros((self.height, self.width, 3), dtype=np.uint8)

        # Gradient horizontal + pattern
        for y in range(self.height):
            for x in range(self.width):
                frame[y, x, 0] = (x * 255) // self.width  # Rouge
                frame[y, x, 1] = (y * 255) // self.height  # Vert
                frame[y, x, 2] = ((x + y) * 255) // (self.width + self.height)  # Bleu

        # Ajouter pattern circulaire simulant visage
        center_x, center_y = self.width // 2, self.height // 2
        radius = min(self.width, self.height) // 6

        for y in range(max(0, center_y - radius), min(self.height, center_y + radius)):
            for x in range(
                max(0, center_x - radius), min(self.width, center_x + radius)
            ):
                dist_sq = (x - center_x) ** 2 + (y - center_y) ** 2
                if dist_sq <= radius**2:
                    frame[y, x] = [200, 180, 150]  # Couleur peau

        self._frame_count += 1
        time.sleep(1.0 / self.fps)  # Simuler FPS

        return frame

    def close(self) -> None:
        """Ferme mock caméra."""
        self._is_opened = False
        logger.info("Mock caméra fermée")

    @property
    def is_opened(self) -> bool:
        return self._is_opened

    def get_fps_stats(self) -> Tuple[float, int]:
        return float(self.fps), self._frame_count


def create_camera(width: int, height: int, fps: int, mock: bool = False) -> Camera:
    """Factory pour créer caméra réelle ou mock."""
    if mock:
        return MockCamera(width, height, fps)
    return Camera(width, height, fps)
