"""
Pollinations TTS Provider – generates speech audio from text via Pollinations.ai API.

Supports multiple voices from OpenAI and ElevenLabs.

Env vars:
  POLLINATIONS_API_KEY - API key from enter.pollinations.ai (optional for limited use)
  POLLINATIONS_TTS_VOICE - Voice to use (default: nova)
  POLLINATIONS_TTS_MODEL - Model to use (default: elevenlabs)

Docs: https://gen.pollinations.ai/docs
"""

from __future__ import annotations

import asyncio
import logging
import os
from pathlib import Path

import httpx
from dotenv import load_dotenv

from providers.ports import TTSProvider

load_dotenv()

logger = logging.getLogger(__name__)

POLLINATIONS_BASE_URL = "https://gen.pollinations.ai"

# Available voices (32 voices from OpenAPI spec)
VOICES = [
    # OpenAI-style voices
    "alloy", "echo", "fable", "onyx", "nova", "shimmer",
    # Additional voices
    "ash", "ballad", "coral", "sage", "verse",
    # Named female voices
    "rachel", "domi", "bella", "elli", "charlotte", "dorothy",
    "sarah", "emily", "lily", "matilda",
    # Named male voices
    "adam", "antoni", "arnold", "josh", "sam", "daniel",
    "charlie", "james", "fin", "callum", "liam", "george", "brian", "bill",
]

# Audio formats (from OpenAPI spec)
AUDIO_FORMATS = ["mp3", "opus", "aac", "flac", "wav", "pcm"]


class PollinationsTTSProvider(TTSProvider):
    """Wraps the Pollinations.ai API for text-to-speech generation."""

    name = "pollinations"

    def __init__(
        self,
        api_key: str | None = None,
        voice: str | None = None,
        speed: float | None = None,
        audio_format: str | None = None,
        model: str | None = None,
    ):
        """Initialize the Pollinations TTS provider.
        
        Args:
            api_key: Optional API key. If not provided, uses POLLINATIONS_API_KEY env var.
            voice: Voice to use (default: nova). See VOICES list for options.
            speed: Speech speed 0.25-4.0 (default: 1.0).
            audio_format: Output format - mp3, opus, aac, flac, wav, pcm (default: mp3).
            model: TTS model to use (default: openai).
        """
        self.api_key = api_key or os.getenv("POLLINATIONS_API_KEY", "")
        self.base_url = POLLINATIONS_BASE_URL
        self.voice = voice or os.getenv("POLLINATIONS_TTS_VOICE", "nova")
        self.model = model or os.getenv("POLLINATIONS_TTS_MODEL", "openai")
        self.format = audio_format or os.getenv("POLLINATIONS_TTS_FORMAT", "mp3")
        self.speed = speed if speed is not None else float(os.getenv("POLLINATIONS_TTS_SPEED", "1.0"))
        
        if self.voice not in VOICES:
            logger.warning(
                "Unknown voice '%s', using 'nova'. Available: %s",
                self.voice,
                VOICES[:10],
            )
            self.voice = "nova"
        
        if self.format not in AUDIO_FORMATS:
            logger.warning(
                "Unknown format '%s', using 'mp3'. Available: %s",
                self.format,
                AUDIO_FORMATS,
            )
            self.format = "mp3"

    def _generate_sync(
        self,
        text: str,
        output_path: str | Path,
        voice: str | None = None,
        speed: float | None = None,
        response_format: str | None = None,
        model: str | None = None,
    ) -> Path:
        """Generate speech audio from text and save to output_path.
        
        Args:
            text: Text to convert to speech (max 4096 characters)
            output_path: Where to save the generated audio
            voice: Voice to use (default: from env or 'nova')
            speed: Speech speed 0.25-4.0 (default: 1.0)
            response_format: Audio format: mp3, opus, aac, flac, wav, pcm
            model: TTS model to use (default: elevenlabs)
            
        Returns:
            Path to the saved audio file
        """
        output_path = Path(output_path)
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        # Use provided values or defaults
        voice = voice or self.voice
        speed = speed if speed is not None else self.speed
        response_format = response_format or self.format
        model = model or self.model
        
        # Validate text length
        if len(text) > 4096:
            logger.warning(
                "Text too long (%d chars), truncating to 4096",
                len(text)
            )
            text = text[:4096]
        
        # Validate speed
        if speed < 0.25 or speed > 4.0:
            logger.warning(
                "Speed %.2f out of range (0.25-4.0), clamping",
                speed
            )
            speed = max(0.25, min(4.0, speed))
        
        # Build request payload (OpenAI-compatible)
        payload = {
            "model": model,
            "input": text,
            "voice": voice,
            "response_format": response_format,
            "speed": speed,
        }
        
        # Build headers
        headers = {
            "Content-Type": "application/json",
            "Accept": f"audio/{response_format}",
        }
        if self.api_key:
            headers["Authorization"] = f"Bearer {self.api_key}"
        
        logger.info(
            "Generating TTS via Pollinations: voice=%s model=%s %d chars → %s",
            voice,
            model,
            len(text),
            output_path,
        )
        
        # Make request
        with httpx.Client(timeout=120) as client:
            response = client.post(
                f"{self.base_url}/v1/audio/speech",
                json=payload,
                headers=headers,
            )
            response.raise_for_status()
            
            # Save audio
            output_path.write_bytes(response.content)
        
        logger.info("TTS audio saved: %s (%d bytes)", output_path, len(response.content))
        return output_path

    async def generate(self, text: str, output_path: Path) -> Path:
        """Generate speech audio asynchronously."""
        return await asyncio.to_thread(self._generate_sync, text, output_path)

    @staticmethod
    def list_voices() -> list[str]:
        """Return list of available voices."""
        return VOICES.copy()

    @staticmethod
    def list_formats() -> list[str]:
        """Return list of available audio formats."""
        return AUDIO_FORMATS.copy()

    @staticmethod
    def get_audio_duration(audio_path: str | Path) -> float:
        """Return the duration of an audio file in seconds using pydub.
        
        Args:
            audio_path: Path to audio file
            
        Returns:
            Duration in seconds
        """
        from pydub import AudioSegment

        seg = AudioSegment.from_file(str(audio_path))
        return seg.duration_seconds
