"""
Capture audio pour le service ASR
"""

import asyncio
import logging
import struct
from typing import Optional, AsyncIterator
import time

try:
    import pyaudio
except ImportError:
    logging.error("Module pyaudio non installé. Installer avec: pip install pyaudio")
    pyaudio = None


class AudioCapture:
    """Gestionnaire de capture audio pour l'ASR."""

    def __init__(
        self, sample_rate: int = 16000, channels: int = 1, chunk_size: int = 1024
    ) -> None:
        self.logger = logging.getLogger(__name__)
        self.sample_rate = sample_rate
        self.channels = channels
        self.chunk_size = chunk_size
        self.format = pyaudio.paInt16 if pyaudio else None

        self.audio_stream = None
        self.pyaudio_instance = None
        self.is_running = False
        self.audio_queue: asyncio.Queue = asyncio.Queue(maxsize=100)

    async def start(self) -> None:
        """Démarre la capture audio."""
        if not pyaudio:
            self.logger.warning("PyAudio non disponible, utilisation d'un mock audio")
            await self._start_mock_capture()
            return

        try:
            self.logger.info("Initialisation de la capture audio")

            self.pyaudio_instance = pyaudio.PyAudio()

            # Trouver le périphérique d'entrée par défaut
            device_info = self.pyaudio_instance.get_default_input_device_info()
            self.logger.info(f"Utilisation du périphérique: {device_info['name']}")

            # Ouvrir le flux audio
            self.audio_stream = self.pyaudio_instance.open(
                format=self.format,
                channels=self.channels,
                rate=self.sample_rate,
                input=True,
                frames_per_buffer=self.chunk_size,
                stream_callback=self._audio_callback,
            )

            self.audio_stream.start_stream()
            self.is_running = True

            self.logger.info("Capture audio démarrée")

        except Exception as e:
            self.logger.error(f"Erreur lors du démarrage de la capture audio: {e}")
            await self._start_mock_capture()

    async def _start_mock_capture(self) -> None:
        """Démarre une capture audio simulée pour les tests."""
        self.logger.info("Démarrage du mock de capture audio")
        self.is_running = True

        # Tâche en arrière-plan pour générer des données audio vides
        asyncio.create_task(self._mock_audio_generator())

    async def _mock_audio_generator(self) -> None:
        """Génère des données audio simulées."""
        silence = b"\x00\x00" * self.chunk_size  # Silence 16-bit

        while self.is_running:
            try:
                await self.audio_queue.put(silence)
                await asyncio.sleep(
                    self.chunk_size / self.sample_rate
                )  # Tempo réaliste
            except asyncio.QueueFull:
                pass  # Ignorer si la queue est pleine
            except Exception as e:
                self.logger.error(f"Erreur dans le générateur mock: {e}")
                break

    def _audio_callback(
        self, in_data: bytes, frame_count: int, time_info, status
    ) -> tuple:
        """Callback PyAudio pour traiter les données audio."""
        try:
            # Ajouter les données à la queue de manière non-bloquante
            try:
                self.audio_queue.put_nowait(in_data)
            except asyncio.QueueFull:
                # Queue pleine, ignorer ce chunk (évite l'accumulation de latence)
                pass

        except Exception as e:
            self.logger.error(f"Erreur dans le callback audio: {e}")

        return (None, pyaudio.paContinue)

    async def get_audio_chunk(self) -> Optional[bytes]:
        """
        Récupère le prochain chunk audio disponible.

        Returns:
            Données audio PCM 16-bit ou None si timeout
        """
        try:
            return await asyncio.wait_for(self.audio_queue.get(), timeout=0.1)
        except asyncio.TimeoutError:
            return None
        except Exception as e:
            self.logger.error(f"Erreur lors de la récupération audio: {e}")
            return None

    async def get_audio_stream(self) -> AsyncIterator[bytes]:
        """
        Générateur asynchrone de chunks audio.

        Yields:
            Données audio PCM 16-bit
        """
        while self.is_running:
            chunk = await self.get_audio_chunk()
            if chunk:
                yield chunk

    def get_audio_level(self, audio_data: bytes) -> float:
        """
        Calcule le niveau audio RMS.

        Args:
            audio_data: Données audio PCM 16-bit

        Returns:
            Niveau RMS normalisé (0.0 à 1.0)
        """
        try:
            # Convertir bytes en échantillons 16-bit
            samples = struct.unpack(f"{len(audio_data)//2}h", audio_data)

            # Calculer RMS
            rms = sum(sample**2 for sample in samples) / len(samples)
            rms = (rms**0.5) / 32768.0  # Normaliser sur 0-1

            return min(1.0, rms)

        except Exception as e:
            self.logger.error(f"Erreur calcul niveau audio: {e}")
            return 0.0

    async def stop(self) -> None:
        """Arrête la capture audio."""
        self.logger.info("Arrêt de la capture audio")
        self.is_running = False

        if self.audio_stream:
            self.audio_stream.stop_stream()
            self.audio_stream.close()

        if self.pyaudio_instance:
            self.pyaudio_instance.terminate()

        # Vider la queue
        while not self.audio_queue.empty():
            try:
                self.audio_queue.get_nowait()
            except asyncio.QueueEmpty:
                break

    def is_available(self) -> bool:
        """Vérifie si la capture audio est disponible."""
        return pyaudio is not None
