from __future__ import annotations

from threading import Lock

from services.ai_voice_runtime_service.providers.tts import (
    ElevenLabsTTSProvider,
    OpenAITTSProvider,
    TTSProvider,
    YandexTTSProvider,
    build_tts_provider,
)
from services.shared.db import get_session
from services.shared.voice_tts_config import load_effective_voice_tts_config


class RuntimeConfiguredTTSProvider(TTSProvider):
    name = "dynamic"

    def __init__(self, *, default_provider_name: str = "yandex") -> None:
        self._default_provider_name = str(default_provider_name or "yandex").strip().lower() or "yandex"
        self._provider_cache: dict[tuple[str, tuple[str | None, ...]], TTSProvider] = {}
        self._lock = Lock()

    def current_provider_name(self) -> str:
        session = get_session()
        try:
            config = load_effective_voice_tts_config(session)
            normalized = str(config.provider or self._default_provider_name).strip().lower()
            if normalized in {"yandex", "elevenlabs", "openai"}:
                return normalized
            return self._default_provider_name
        except Exception:
            return self._default_provider_name
        finally:
            session.close()

    def _provider_for_language(self, language: str | None) -> TTSProvider:
        session = get_session()
        try:
            config = load_effective_voice_tts_config(session)
        except Exception:
            return build_tts_provider(self._default_provider_name)
        finally:
            session.close()

        provider_name = str(config.provider or self._default_provider_name).strip().lower()
        if provider_name not in {"yandex", "elevenlabs", "openai"}:
            provider_name = self._default_provider_name

        cache_key: tuple[str, tuple[str | None, ...]]
        provider: TTSProvider
        if provider_name == "yandex":
            cache_key = (
                provider_name,
                (
                    config.yandex.ru.voice,
                    config.yandex.kz.voice,
                ),
            )
            with self._lock:
                provider = self._provider_cache.get(cache_key)
                if provider is None:
                    provider = YandexTTSProvider(
                        ru_voice=config.yandex.ru.voice,
                        kz_voice=config.yandex.kz.voice,
                    )
                    self._provider_cache[cache_key] = provider
            return provider

        if provider_name == "elevenlabs":
            cache_key = (
                provider_name,
                (
                    config.elevenlabs.ru.voice,
                    config.elevenlabs.kz.voice,
                    config.elevenlabs.ru.model_id,
                    config.elevenlabs.kz.model_id,
                    config.elevenlabs.ru.language_code,
                    config.elevenlabs.kz.language_code,
                ),
            )
            with self._lock:
                provider = self._provider_cache.get(cache_key)
                if provider is None:
                    provider = ElevenLabsTTSProvider(
                        ru_voice=config.elevenlabs.ru.voice,
                        kz_voice=config.elevenlabs.kz.voice,
                        ru_model=config.elevenlabs.ru.model_id,
                        kz_model=config.elevenlabs.kz.model_id,
                        ru_language_code=config.elevenlabs.ru.language_code,
                        kz_language_code=config.elevenlabs.kz.language_code,
                    )
                    self._provider_cache[cache_key] = provider
            return provider

        cache_key = (
            "openai",
            (
                config.openai.ru.voice,
                config.openai.kz.voice,
                config.openai.ru.model_id,
                config.openai.kz.model_id,
            ),
        )
        with self._lock:
            provider = self._provider_cache.get(cache_key)
            if provider is None:
                provider = OpenAITTSProvider(
                    ru_voice=config.openai.ru.voice,
                    kz_voice=config.openai.kz.voice,
                    ru_model=config.openai.ru.model_id,
                    kz_model=config.openai.kz.model_id,
                )
                self._provider_cache[cache_key] = provider
        return provider

    def synthesize(self, text: str, *, language: str | None = None, style_hints: dict[str, object] | None = None):
        provider = self._provider_for_language(language)
        return provider.synthesize(text, language=language, style_hints=style_hints)

    def synthesize_chunks(self, text: str, *, language: str | None = None, style_hints: dict[str, object] | None = None):
        provider = self._provider_for_language(language)
        yield from provider.synthesize_chunks(text, language=language, style_hints=style_hints)
