"""Download and normalize recording files for Sarvam STT (WAV, OGG, MP3)."""

from __future__ import annotations

import logging
import os
import shutil
import subprocess
import tempfile
from dataclasses import dataclass
from typing import List, Optional
from urllib.parse import urlparse

from .audio_chunker import wav_duration_seconds

logger = logging.getLogger(__name__)


@dataclass
class PreparedAudio:
    path: str
    duration: float
    cleanup_paths: List[str]


def _suffix_from_url(url: str) -> str:
    path = urlparse(str(url or "")).path.lower()
    for ext in (".wav", ".ogg", ".mp3", ".mpeg", ".m4a", ".flac"):
        if path.endswith(ext):
            return ext
    return ".wav"


def _detect_format(data: bytes, url: str) -> str:
    if len(data) >= 4 and data[0:4] == b"RIFF":
        return ".wav"
    if len(data) >= 4 and data[0:4] == b"OggS":
        return ".ogg"
    if len(data) >= 3 and data[0:3] == b"ID3":
        return ".mp3"
    if len(data) >= 2 and data[0] == 0xFF and (data[1] & 0xE0) == 0xE0:
        return ".mp3"
    return _suffix_from_url(url)


def _duration_for_path(path: str, suffix: str) -> float:
    if suffix == ".wav":
        return wav_duration_seconds(path)
    try:
        from mutagen import File as MutagenFile

        meta = MutagenFile(path)
        if meta is not None and getattr(meta, "info", None) is not None:
            length = float(meta.info.length or 0)
            if length > 0:
                return length
    except Exception as exc:
        logger.debug("mutagen duration failed for %s: %s", path, exc)
    if suffix == ".wav":
        return wav_duration_seconds(path)
    raise RuntimeError(f"Could not determine audio duration for {suffix}")


def _ffmpeg_convert_to_wav(src_path: str, callid: str) -> str:
    ffmpeg = shutil.which("ffmpeg")
    if not ffmpeg:
        raise RuntimeError("ffmpeg not installed — cannot convert non-WAV audio")
    out_fd, out_path = tempfile.mkstemp(suffix=".wav", dir="/tmp")
    os.close(out_fd)
    cmd = [
        ffmpeg,
        "-y",
        "-hide_banner",
        "-loglevel",
        "error",
        "-i",
        src_path,
        "-acodec",
        "pcm_s16le",
        "-ar",
        "16000",
        "-ac",
        "1",
        out_path,
    ]
    try:
        subprocess.run(cmd, check=True, capture_output=True, text=True, timeout=120)
    except subprocess.CalledProcessError as exc:
        if os.path.exists(out_path):
            os.unlink(out_path)
        stderr = (exc.stderr or "").strip()
        raise RuntimeError(f"ffmpeg conversion failed: {stderr or exc}") from exc
    logger.info("[%s] Converted %s → WAV via ffmpeg", callid, os.path.basename(src_path))
    return out_path


def prepare_audio(content: bytes, url: str, callid: str) -> PreparedAudio:
    """
    Save downloaded bytes with the correct extension and return a WAV path for Sarvam.

    WAV inputs are used as-is. OGG/MP3/etc. are converted with ffmpeg when available.
    """
    suffix = _detect_format(content, url)
    cleanup: List[str] = []

    src_fd, src_path = tempfile.mkstemp(suffix=suffix, dir="/tmp")
    os.close(src_fd)
    cleanup.append(src_path)
    with open(src_path, "wb") as fh:
        fh.write(content)

    try:
        if suffix == ".wav":
            duration = _duration_for_path(src_path, suffix)
            return PreparedAudio(path=src_path, duration=duration, cleanup_paths=cleanup)

        duration = _duration_for_path(src_path, suffix)
        wav_path = _ffmpeg_convert_to_wav(src_path, callid)
        cleanup.append(wav_path)
        return PreparedAudio(path=wav_path, duration=duration, cleanup_paths=cleanup)
    except Exception:
        for path in cleanup:
            try:
                if path and os.path.exists(path):
                    os.unlink(path)
            except OSError:
                pass
        raise


def cleanup_prepared(prepared: PreparedAudio) -> None:
    for path in prepared.cleanup_paths:
        try:
            if path and os.path.exists(path):
                os.unlink(path)
        except OSError:
            pass
