"""Client MQTT pour publication des données audio."""

import json
import logging
import time
from typing import Callable, Optional, Dict, Any

import paho.mqtt.client as mqtt

from .config import MQTTConfig, VADConfig
from .vad import VADResult


logger = logging.getLogger(__name__)


class MQTTAudioClient:
    """Client MQTT spécialisé pour les données audio."""

    def __init__(self, config: MQTTConfig):
        """
        Initialise le client MQTT.

        Args:
            config: Configuration MQTT
        """
        self.config = config
        self.client = mqtt.Client(client_id=config.client_id, protocol=mqtt.MQTTv311)
        self.connected = False
        self.config_callback: Optional[Callable[[VADConfig], None]] = None

        # Callbacks MQTT
        self.client.on_connect = self._on_connect
        self.client.on_disconnect = self._on_disconnect
        self.client.on_message = self._on_message
        self.client.on_log = self._on_log

    def _on_connect(self, client: mqtt.Client, userdata, flags, rc: int) -> None:
        """Callback connexion MQTT."""
        if rc == 0:
            self.connected = True
            logger.info(f"MQTT connecté: {self.config.host}:{self.config.port}")

            # Souscription config
            client.subscribe(self.config.topic_config, qos=1)
            logger.info(f"Souscrit à {self.config.topic_config}")

            # Publication capabilities (retained)
            self.publish_capabilities()

        else:
            self.connected = False
            logger.error(f"Échec connexion MQTT: {rc}")

    def _on_disconnect(self, client: mqtt.Client, userdata, rc: int) -> None:
        """Callback déconnexion MQTT."""
        self.connected = False
        if rc != 0:
            logger.warning(f"Déconnexion MQTT inattendue: {rc}")
        else:
            logger.info("MQTT déconnecté")

    def _on_message(self, client: mqtt.Client, userdata, msg: mqtt.MQTTMessage) -> None:
        """Callback réception message MQTT."""
        try:
            topic = msg.topic
            payload = msg.payload.decode("utf-8")

            logger.debug(f"Message reçu: {topic} -> {payload}")

            if topic == self.config.topic_config:
                self._handle_config_update(payload)

        except Exception as e:
            logger.error(f"Erreur traitement message MQTT: {e}")

    def _on_log(self, client: mqtt.Client, userdata, level: int, buf: str) -> None:
        """Callback logs MQTT."""
        if level <= mqtt.MQTT_LOG_WARNING:
            logger.debug(f"MQTT: {buf}")

    def _handle_config_update(self, payload: str) -> None:
        """Traite mise à jour de configuration."""
        try:
            config_data = json.loads(payload)
            new_config = VADConfig.from_dict(config_data)

            logger.info(f"Configuration VAD mise à jour: {config_data}")

            if self.config_callback:
                self.config_callback(new_config)

        except Exception as e:
            logger.error(f"Erreur parsing config VAD: {e}")

    def connect(self, timeout: float = 10.0) -> bool:
        """
        Connecte au broker MQTT.

        Args:
            timeout: Timeout de connexion

        Returns:
            True si connecté
        """
        try:
            self.client.connect(
                self.config.host, self.config.port, self.config.keepalive
            )
            self.client.loop_start()

            # Attendre connexion
            start_time = time.time()
            while not self.connected and (time.time() - start_time) < timeout:
                time.sleep(0.1)

            return self.connected

        except Exception as e:
            logger.error(f"Erreur connexion MQTT: {e}")
            return False

    def disconnect(self) -> None:
        """Déconnecte du broker MQTT."""
        try:
            self.client.loop_stop()
            self.client.disconnect()
            self.connected = False
        except Exception as e:
            logger.error(f"Erreur déconnexion MQTT: {e}")

    def publish_rms(self, rms: float) -> None:
        """
        Publie une mesure RMS.

        Args:
            rms: Valeur RMS normalisée
        """
        if not self.connected:
            return

        message = {"ts_ms": int(time.time() * 1000), "rms": round(rms, 3)}

        try:
            payload = json.dumps(message, separators=(",", ":"))
            self.client.publish(self.config.topic_rms, payload, qos=0)
        except Exception as e:
            logger.error(f"Erreur publication RMS: {e}")

    def publish_vad(self, vad_result: VADResult) -> None:
        """
        Publie un résultat VAD.

        Args:
            vad_result: Résultat du VAD
        """
        if not self.connected:
            return

        message = {
            "ts_ms": vad_result.timestamp_ms,
            "active": vad_result.active,
            "rms": round(vad_result.rms, 3),
        }

        # Ajouter end_of_speech si présent
        if vad_result.end_of_speech:
            message["end_of_speech"] = True

        try:
            payload = json.dumps(message, separators=(",", ":"))
            self.client.publish(self.config.topic_vad, payload, qos=0)
        except Exception as e:
            logger.error(f"Erreur publication VAD: {e}")

    def publish_capabilities(self) -> None:
        """Publie les capacités du service (retained)."""
        if not self.connected:
            return

        capabilities = {
            "mic": "respeaker-2mic",
            "rate": 16000,
            "vad": True,
            "channels": 1,
        }

        try:
            payload = json.dumps(capabilities, separators=(",", ":"))
            self.client.publish(
                self.config.topic_capabilities, payload, qos=1, retain=True
            )
            logger.info("Capacités publiées (retained)")
        except Exception as e:
            logger.error(f"Erreur publication capacités: {e}")

    def set_config_callback(self, callback: Callable[[VADConfig], None]) -> None:
        """
        Définit le callback pour les mises à jour de config.

        Args:
            callback: Fonction appelée avec la nouvelle config
        """
        self.config_callback = callback

    def is_connected(self) -> bool:
        """Retourne l'état de connexion."""
        return self.connected

    def __enter__(self):
        """Context manager."""
        self.connect()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """Context manager."""
        self.disconnect()


def create_mqtt_client(
    host: str = "127.0.0.1", port: int = 1883, client_id: str = "skull-audioin"
) -> MQTTAudioClient:
    """Fonction utilitaire pour créer un client MQTT."""
    config = MQTTConfig(host=host, port=port, client_id=client_id)
    return MQTTAudioClient(config)
