import logging
from typing import Callable, Optional, Union

from om1_speech import AudioOutputLiveStream

from .singleton import singleton


@singleton
class ElevenLabsTTSProvider:
    """
    Text-to-Speech Provider that manages an audio output stream.

    A singleton class that handles text-to-speech conversion and audio output
    through a dedicated thread. It provides integration with Eleven Labs TTS service
    for generating speech from text with configurable voice, model, and output format.
    """

    def __init__(
        self,
        url: str = "https://api.openmind.com/api/core/elevenlabs/tts",
        api_key: Optional[str] = None,
        elevenlabs_api_key: Optional[str] = None,
        voice_id: Optional[str] = "JBFqnCBsd6RMkjVDRZzb",
        model_id: Optional[str] = "eleven_flash_v2_5",
        output_format: Optional[str] = "pcm_16000",
        rate: Optional[int] = 16000,
        enable_tts_interrupt: bool = False,
    ):
        """
        Initialize the ElevenLabsTTSProvider instance.

        Sets up the configuration for the Eleven Labs TTS service, including API keys,
        voice/model selection, output format, and interrupt settings. It initializes
        the underlying audio output stream.

        Parameters
        ----------
        url : str, optional
            The URL endpoint for the TTS service.
            Defaults to "https://api.openmind.com/api/core/elevenlabs/tts".
        api_key : str, optional
            The primary API key for the TTS service. If provided, it's used in the
            request headers as "x-api-key". Defaults to None.
        elevenlabs_api_key : str, optional
            An alternative Eleven Labs specific API key. If provided, it's included
            in the request payload for TTS generation. Defaults to None.
        voice_id : str, optional
            The ID/name of the voice to use for TTS synthesis.
            Defaults to "JBFqnCBsd6RMkjVDRZzb".
        model_id : str, optional
            The ID/name of the model to use for TTS synthesis.
            Defaults to "eleven_flash_v2_5".
        output_format : str, optional
            The desired audio output format (e.g., pcm_16000, wav).
            Defaults to "pcm_16000".
        rate : int, optional
            The audio sample rate in Hz.
            Defaults to 16000.
        enable_tts_interrupt : bool, optional
            If True, enables the ability to interrupt ongoing TTS playback when ASR
            detects new speech input. Defaults to False.
        """
        self.api_key = api_key
        self.elevenlabs_api_key = elevenlabs_api_key
        self._enable_tts_interrupt = enable_tts_interrupt

        # Initialize TTS provider
        self.running: bool = False
        self._audio_stream: AudioOutputLiveStream = AudioOutputLiveStream(
            url=url,
            tts_model=model_id or "eleven_flash_v2_5",
            tts_voice=voice_id or "JBFqnCBsd6RMkjVDRZzb",
            response_format=output_format or "pcm_16000",
            rate=rate or 16000,
            api_key=api_key,
            enable_tts_interrupt=enable_tts_interrupt,
            extra_body=({"elevenlabs_api_key": self.elevenlabs_api_key} if self.elevenlabs_api_key else {}),
        )

        # Set Eleven Labs TTS parameters
        self._voice_id = voice_id
        self._model_id = model_id
        self._output_format = output_format
        self._rate = rate

    def configure(
        self,
        url: str = "https://api.openmind.com/api/core/elevenlabs/tts",
        api_key: Optional[str] = None,
        elevenlabs_api_key: Optional[str] = None,
        voice_id: Optional[str] = "JBFqnCBsd6RMkjVDRZzb",
        model_id: Optional[str] = "eleven_flash_v2_5",
        output_format: Optional[str] = "pcm_16000",
        rate: Optional[int] = 16000,
        enable_tts_interrupt: bool = False,
    ):
        """
        Configure the TTS provider with given parameters.

        Parameters
        ----------
        url : str
            The URL endpoint for the TTS service.
        api_key : str
            The API key for the TTS service.
        elevenlabs_api_key : Optional[str]
            An alternative Eleven Labs specific API key.
        voice_id : str, optional
            The name of the voice for Eleven Labs TTS service.
        model_id : str, optional
            The name of the model for Eleven Labs TTS service.
        output_format : str, optional
            The output format for the audio stream.
        rate : int, optional
            The audio sample rate in Hz.
            Defaults to 16000.
        enable_tts_interrupt : bool
            If True, enables TTS interrupt when ASR detects speech.
        """
        restart_needed = (
            url != self._audio_stream._url
            or api_key != self.api_key
            or elevenlabs_api_key != self.elevenlabs_api_key
            or voice_id != self._voice_id
            or model_id != self._model_id
            or output_format != self._output_format
            or enable_tts_interrupt != self._enable_tts_interrupt
            or rate != self._rate
        )

        if not restart_needed:
            return

        if self.running:
            self.stop()

        self.api_key = api_key
        self.elevenlabs_api_key = elevenlabs_api_key
        self._voice_id = voice_id
        self._model_id = model_id
        self._output_format = output_format
        self._enable_tts_interrupt = enable_tts_interrupt
        self._rate = rate

        self._audio_stream: AudioOutputLiveStream = AudioOutputLiveStream(
            url=url,
            tts_model=model_id or "eleven_flash_v2_5",
            tts_voice=voice_id or "JBFqnCBsd6RMkjVDRZzb",
            response_format=output_format or "pcm_16000",
            rate=rate or 16000,
            enable_tts_interrupt=enable_tts_interrupt,
            api_key=api_key,
            extra_body=({"elevenlabs_api_key": self.elevenlabs_api_key} if self.elevenlabs_api_key else {}),
        )
        self._audio_stream.start()

    def register_tts_state_callback(self, tts_state_callback: Optional[Callable]):
        """
        Register a callback for TTS state changes.

        Parameters
        ----------
        tts_state_callback : Optional[Callable]
            The callback function to receive TTS state changes.
        """
        if tts_state_callback is not None:
            self._audio_stream.set_tts_state_callback(tts_state_callback)

    def create_pending_message(self, text: str, voice_id: Optional[str] = None) -> dict:
        """
        Create a pending message for TTS processing.

        Parameters
        ----------
        text : str
            Text to be converted to speech
        voice_id : Optional[str]
            Optional voice ID to override the default voice for this message

        Returns
        -------
        dict
            A dictionary containing the TTS request parameters.
        """
        logging.info(
            f"audio_stream: {text}, voice_id: {voice_id or self._voice_id}, model_id: {self._model_id}, output_format: {self._output_format}"
        )
        return {
            "text": text,
            "voice_id": self._voice_id if voice_id is None else voice_id,
            "model_id": self._model_id,
            "output_format": self._output_format,
        }

    def add_pending_message(self, message: Union[str, dict]):
        """
        Add a pending message to the TTS provider.

        Parameters
        ----------
        message : Union[str, dict]
            The message to be added, typically containing text and TTS parameters.
        """
        if not self.running:
            logging.warning("TTS provider is not running. Call start() before adding messages.")
            return

        if isinstance(message, str):
            message = self.create_pending_message(message)
        self._audio_stream.add_request(message)

    def get_pending_message_count(self) -> int:
        """
        Get the count of pending messages in the TTS provider.

        Returns
        -------
        int
            The number of pending messages.
        """
        return self._audio_stream._pending_requests.qsize()

    def start(self):
        """
        Start the TTS provider and its audio stream.
        """
        if self.running:
            logging.warning("Eleven Labs TTS provider is already running")
            return

        self.running = True
        self._audio_stream.start()

    def stop(self):
        """
        Stop the TTS provider and cleanup resources.
        """
        if not self.running:
            logging.warning("Eleven Labs TTS provider is not running")
            return

        self.running = False
        self._audio_stream.stop()
