"""
Gestion de l'arrêt d'urgence (E-stop) GPIO
"""

import time
from pathlib import Path
import threading
from typing import Callable, Optional

from .utils import setup_json_logger

try:
    import gpiozero  # type: ignore

    GPIO_AVAILABLE = True
except Exception:
    GPIO_AVAILABLE = False


class EmergencyStop:
    """Gestion de l'arrêt d'urgence physique ou simulé.

    - Si le GPIO est disponible (Raspberry Pi) on utilise gpiozero.Button
    - Sinon on bascule en mode simulation (utile pour dev/test)
    """

    def __init__(
        self,
        gpio_pin: int,
        on_trigger: Optional[Callable[[bool], None]] = None,
        simulate: Optional[bool] = None,
    ):
        self.gpio_pin = gpio_pin
        self.on_trigger = on_trigger
        self._button: Optional["gpiozero.Button"] = None
        self._simulated = False
        self._pressed = False
        self._lock = threading.Lock()
        self.logger = setup_json_logger(Path("/opt/Skull/logs/motion.log"))

        # Si simulate est explicitement donné on le respecte, sinon on déduit de la présence GPIO
        if simulate is None:
            self._simulated = not GPIO_AVAILABLE
        else:
            self._simulated = bool(simulate)

    # ------------------------------
    # API publique
    # ------------------------------
    def initialize(self) -> bool:
        """Initialise le bouton d'arrêt d'urgence.

        Retourne True si l'initialisation est réussie.
        """
        if self._simulated:
            self.logger.warning("E-stop en mode simulation (GPIO indisponible)")
            return True

        try:
            # Tirage au +3V via une résistance pull-up interne : presser = état bas.
            self._button = gpiozero.Button(
                self.gpio_pin, pull_up=True, bounce_time=0.02
            )
            self._button.when_pressed = self._handle_pressed
            self._button.when_released = self._handle_released
            self.logger.info(f"E-stop initialisé sur GPIO {self.gpio_pin}")
            return True
        except Exception as e:
            self.logger.error(
                f"Echec initialisation E-stop sur GPIO {self.gpio_pin}: {e}"
            )
            return False

    def cleanup(self) -> None:
        with self._lock:
            if self._button is not None:
                try:
                    self._button.close()
                except Exception:
                    pass
                self._button = None
            self.logger.info("E-stop fermé")

    def is_pressed(self) -> bool:
        with self._lock:
            if self._button is not None:
                try:
                    return bool(self._button.is_pressed)
                except Exception:
                    # Si gpiozero jette, on bascule sur état interne
                    return self._pressed
            return self._pressed

    # --- Simulation helpers ---
    def simulate_press(self) -> None:
        if not self._simulated:
            self.logger.warning(
                "simulate_press() disponible uniquement en mode simulation"
            )
            return
        self._handle_pressed()

    def simulate_release(self) -> None:
        if not self._simulated:
            self.logger.warning(
                "simulate_release() disponible uniquement en mode simulation"
            )
            return
        self._handle_released()

    # ------------------------------
    # Callbacks internes
    # ------------------------------
    def _handle_pressed(self) -> None:
        with self._lock:
            self._pressed = True
        self.logger.warning("E-stop ACTIVÉ (pressed=true)")
        if self.on_trigger:
            try:
                self.on_trigger(True)
            except Exception as e:
                self.logger.error(f"Callback E-stop (pressed) a échoué: {e}")

    def _handle_released(self) -> None:
        with self._lock:
            self._pressed = False
        self.logger.info("E-stop relâché (pressed=false)")
        if self.on_trigger:
            try:
                self.on_trigger(False)
            except Exception as e:
                self.logger.error(f"Callback E-stop (released) a échoué: {e}")

    # ------------------------------
    # SHIM DE COMPATIBILITÉ
    # ------------------------------
    def _initialiser_gpio_avec_retry(
        self, retries: int = 3, delay: float = 0.5
    ) -> bool:
        """Ancienne API conservée pour compatibilité.

        Essaie d'appeler :meth:`initialize` jusqu'à `retries` fois avec délai entre les tentatives.
        """
        for _ in range(max(1, int(retries))):
            if self.initialize():
                return True
            time.sleep(max(0.0, float(delay)))
        return False
