"""Service principal Voice - Coordination TTS, MP3 et jaw sync."""

import time
import threading
from typing import Optional

from .voice_types import (
    TTSRequest,
    MP3Request,
    JawConfig,
    VoiceState,
    Capabilities,
    PlaybackState,
    AudioSource,
    RMSData,
)
from .config import VoiceConfig
from .logger import setup_logger, log_with_metrics
from .mqtt_handler import MQTTHandler
from .tts import TTSEngine
from .mp3 import MP3Player
from .jaw_sync import JawSyncProcessor
from .audio import AudioOutput


class VoiceService:
    """Service principal de synthèse vocale et lecture audio."""

    def __init__(self):
        """Initialise le service voice."""
        self.logger = setup_logger(__name__)

        # Configuration jaw sync
        self.jaw_config = JawConfig(**VoiceConfig.DEFAULT_JAW_CONFIG)

        # Composants
        self.jaw_processor = JawSyncProcessor(self.jaw_config)
        self.tts_engine = TTSEngine(self.jaw_processor)
        self.mp3_player = MP3Player(self.jaw_processor)
        self.audio_output = AudioOutput()
        self.mqtt_handler = MQTTHandler()

        # État
        self.state = VoiceState()
        self.capabilities = Capabilities()

        # Threading
        self._running = False
        self._lock = threading.Lock()

        # Setup callbacks
        self._setup_callbacks()

    def _setup_callbacks(self):
        """Configure les callbacks MQTT et internes."""
        # Callbacks MQTT
        self.mqtt_handler.set_callback("tts", self.handle_tts_request)
        self.mqtt_handler.set_callback("mp3_play", self.handle_mp3_play)
        self.mqtt_handler.set_callback("mp3_stop", self.handle_mp3_stop)
        self.mqtt_handler.set_callback("mp3_pause", self.handle_mp3_pause)
        self.mqtt_handler.set_callback("mp3_resume", self.handle_mp3_resume)
        self.mqtt_handler.set_callback("jaw_config", self.handle_jaw_config)

        # Callback RMS du MP3 player
        self.mp3_player.set_rms_callback(self.handle_rms_data)

    def start(self) -> bool:
        """Démarre le service."""
        self.logger.info("Démarrage du service Voice...")

        # Connexion MQTT
        if not self.mqtt_handler.connect():
            self.logger.error("Impossible de se connecter à MQTT")
            return False

        # Publie les capacités
        self.mqtt_handler.publish_capabilities(self.capabilities)

        # Publie l'état initial
        self.mqtt_handler.publish_state(self.state)

        self._running = True
        self.logger.info("Service Voice démarré")

        return True

    def stop(self):
        """Arrête le service."""
        self.logger.info("Arrêt du service Voice...")

        self._running = False

        # Arrête la lecture en cours
        self.mp3_player.stop()

        # Déconnexion MQTT
        self.mqtt_handler.disconnect()

        self.logger.info("Service Voice arrêté")

    def is_running(self) -> bool:
        """Vérifie si le service est actif."""
        return self._running

    def handle_tts_request(self, request: TTSRequest):
        """Gère une requête TTS."""
        self.logger.info(f"Requête TTS: '{request.text[:50]}...'")

        start_time = time.time()

        try:
            with self._lock:
                # Arrête lecture MP3 si active
                if self.mp3_player.get_state() != PlaybackState.IDLE:
                    self.mp3_player.stop()

                # Met à jour l'état
                self.state.playing = True
                self.state.src = AudioSource.TTS.value
                self.state.file = None
                self.mqtt_handler.publish_state(self.state)

            # Synthèse TTS avec phonèmes
            wav_data, phonemes, duration_ms = self.tts_engine.synthesize_with_phonemes(
                request
            )

            # Génère visèmes si phonèmes disponibles
            if phonemes:
                start_time_ms = int(time.time() * 1000)
                visemes = self.tts_engine.generate_visemes(
                    phonemes, start_time_ms, duration_ms
                )

                # Publie visèmes en parallèle
                threading.Thread(
                    target=self._publish_visemes_sequence, args=(visemes,), daemon=True
                ).start()
            else:
                # Fallback: RMS depuis audio WAV
                threading.Thread(
                    target=self._publish_tts_rms, args=(wav_data,), daemon=True
                ).start()

            # Lecture audio
            success = self.audio_output.play_wav_data(wav_data, blocking=True)

            processing_time = (time.time() - start_time) * 1000

            log_with_metrics(
                self.logger,
                "INFO",
                f"TTS terminé: {len(request.text)} chars",
                processing_time_ms=processing_time,
                duration_ms=duration_ms,
                phonemes_count=len(phonemes),
                success=success,
            )

        except Exception as e:
            self.logger.error(f"Erreur TTS: {e}")
        finally:
            # Remet à jour l'état
            with self._lock:
                self.state.playing = False
                self.state.src = None
                self.mqtt_handler.publish_state(self.state)

    def handle_mp3_play(self, request: MP3Request):
        """Gère une requête de lecture MP3."""
        self.logger.info(f"Requête MP3: {request.file}")

        try:
            success = self.mp3_player.play(request)

            if success:
                with self._lock:
                    self.state.playing = True
                    self.state.src = AudioSource.MP3.value
                    self.state.file = request.file
                    self.mqtt_handler.publish_state(self.state)

        except Exception as e:
            self.logger.error(f"Erreur lecture MP3: {e}")

    def handle_mp3_stop(self):
        """Gère l'arrêt MP3."""
        self.logger.info("Arrêt MP3")

        try:
            self.mp3_player.stop()

            with self._lock:
                self.state.playing = False
                self.state.src = None
                self.state.file = None
                self.mqtt_handler.publish_state(self.state)

        except Exception as e:
            self.logger.error(f"Erreur arrêt MP3: {e}")

    def handle_mp3_pause(self):
        """Gère la pause MP3."""
        self.logger.info("Pause MP3")

        try:
            self.mp3_player.pause()

            # Note: on garde state.playing = True pour pause
            # Seul l'état interne du player change

        except Exception as e:
            self.logger.error(f"Erreur pause MP3: {e}")

    def handle_mp3_resume(self):
        """Gère la reprise MP3."""
        self.logger.info("Reprise MP3")

        try:
            self.mp3_player.resume()

        except Exception as e:
            self.logger.error(f"Erreur reprise MP3: {e}")

    def handle_jaw_config(self, config: JawConfig):
        """Gère la mise à jour de configuration jaw sync."""
        self.logger.info(
            f"Mise à jour jaw config: gain={config.gain}, smoothing={config.smoothing}, latency={config.latency_ms}ms"
        )

        try:
            self.jaw_config = config
            self.jaw_processor.update_config(config)

            log_with_metrics(
                self.logger,
                "INFO",
                "Configuration jaw sync mise à jour",
                gain=config.gain,
                smoothing=config.smoothing,
                latency_ms=config.latency_ms,
            )

        except Exception as e:
            self.logger.error(f"Erreur config jaw sync: {e}")

    def handle_rms_data(self, rms_data: RMSData):
        """Gère les données RMS (callback depuis MP3Player)."""
        try:
            self.mqtt_handler.publish_rms(rms_data.to_dict())
        except Exception as e:
            self.logger.error(f"Erreur publication RMS: {e}")

    def _publish_visemes_sequence(self, visemes):
        """Publie une séquence de visèmes avec timing."""
        try:
            start_time = time.time()

            for viseme in visemes:
                # Calcule le délai depuis le début
                elapsed_ms = (time.time() - start_time) * 1000
                target_time_ms = viseme.ts_ms - int(start_time * 1000)

                if target_time_ms > elapsed_ms:
                    delay = (target_time_ms - elapsed_ms) / 1000.0
                    time.sleep(delay)

                # Publie le visème
                self.mqtt_handler.publish_viseme(viseme.to_dict())

        except Exception as e:
            self.logger.error(f"Erreur publication visèmes: {e}")

    def _publish_tts_rms(self, wav_data: bytes):
        """Publie RMS depuis données WAV TTS."""
        try:
            # Convertit WAV en numpy
            audio_data, sample_rate = self.tts_engine.wav_to_numpy(wav_data)

            if len(audio_data) == 0:
                return

            # Génère et publie RMS
            for rms_data in self.jaw_processor.process_audio_frames(
                audio_data, sample_rate
            ):
                self.mqtt_handler.publish_rms(rms_data.to_dict())

                # Délai pour synchroniser avec la lecture audio
                time.sleep(VoiceConfig.FRAME_SIZE / 1000.0)

        except Exception as e:
            self.logger.error(f"Erreur RMS TTS: {e}")

    def get_status(self) -> dict:
        """Retourne le statut complet du service."""
        return {
            "running": self._running,
            "mqtt_connected": self.mqtt_handler.is_connected(),
            "state": self.state.to_dict(),
            "capabilities": self.capabilities.to_dict(),
            "jaw_config": {
                "gain": self.jaw_config.gain,
                "smoothing": self.jaw_config.smoothing,
                "latency_ms": self.jaw_config.latency_ms,
            },
            "audio_system": self.audio_output.audio_system,
            "mp3_state": self.mp3_player.get_state().value,
        }
