import os
import subprocess
import uuid
from pathlib import Path
from urllib.parse import urljoin

import requests

TTS_CONNECT_TIMEOUT_SECONDS = 15
TTS_READ_TIMEOUT_SECONDS = 3 * 60 * 60
TTS_REQUEST_TIMEOUT = (TTS_CONNECT_TIMEOUT_SECONDS, TTS_READ_TIMEOUT_SECONDS)


def convert_volume_to_percent(volume: float) -> int:
    value = float(volume if volume is not None else 1.0)
    if value <= 2:
        value *= 100
    return max(0, min(100, round(value)))


def normalize_cosyvoice_rate(rate: float) -> float:
    value = float(rate if rate is not None else 1.0)
    return max(0.5, min(2.0, value))


def get_audio_duration_from_file(media_path: str) -> float:
    try:
        cmd = (
            f'ffprobe -v error -select_streams a:0 '
            f'-show_entries stream=duration '
            f'-of default=noprint_wrappers=1:nokey=1 '
            f'"{media_path}"'
        )
        result = subprocess.run(
            cmd, shell=True, capture_output=True, text=True, timeout=30
        )
        return float(result.stdout.strip()) if result.stdout.strip() else 0.0
    except Exception as e:
        print(f"获取媒体时长失败（{media_path}）：{e}")
        return 0.0


def _download_file(url: str, output_path: str, base_url: str = ""):
    if not url.startswith(("http://", "https://")):
        url = urljoin(base_url, url.lstrip("/"))
    response = requests.get(url, timeout=120)
    response.raise_for_status()
    Path(os.path.dirname(output_path)).mkdir(parents=True, exist_ok=True)
    with open(output_path, "wb") as file:
        file.write(response.content)


def _format_srt_time(seconds: float):
    milliseconds = int(max(seconds, 0) * 1000)
    hours = milliseconds // 3600000
    milliseconds %= 3600000
    minutes = milliseconds // 60000
    milliseconds %= 60000
    secs = milliseconds // 1000
    millis = milliseconds % 1000
    return f"{hours:02}:{minutes:02}:{secs:02},{millis:03}"


def _write_fallback_srt(text: str, subtitle_file: str, duration: float):
    Path(os.path.dirname(subtitle_file)).mkdir(parents=True, exist_ok=True)
    with open(subtitle_file, "w", encoding="utf-8") as file:
        file.write(f"1\n00:00:00,000 --> {_format_srt_time(duration)}\n{text}\n")


def _require_success(response_data: dict, default_message: str):
    if response_data.get("status") != 1:
        raise RuntimeError(
            response_data.get("error")
            or response_data.get("message")
            or response_data.get("msg")
            or default_message
        )


def _save_remote_tts_result(
    response_data: dict,
    audio_file: str,
    subtitle_file: str,
    domestic_interface: str,
    text: str,
    missing_audio_message: str,
) -> float:
    data = response_data.get("data") or {}
    if not isinstance(data, dict):
        data = response_data

    audio_url = (
        data.get("audio_url")
        or data.get("audioUrl")
        or data.get("audio")
        or data.get("url")
        or data.get("fileFullPath")
        or data.get("fullPath")
    )
    srt_url = (
        data.get("srt_url")
        or data.get("srtUrl")
        or data.get("subtitle_url")
        or data.get("subtitleUrl")
        or data.get("srt")
    )
    if not audio_url:
        raise RuntimeError(missing_audio_message)

    _download_file(audio_url, audio_file, domestic_interface)
    if srt_url:
        _download_file(srt_url, subtitle_file, domestic_interface)

    duration = get_audio_duration_from_file(audio_file)
    if duration <= 0:
        duration = max(1.0, len(text or "") / 3.0)
    if not srt_url:
        _write_fallback_srt(text or "", subtitle_file, duration)
    return duration


def cosyvoice_clone_tts(
    text: str,
    voice_id: str,
    audio_file: str,
    subtitle_file: str,
    domestic_interface: str,
    user_id,
    token: str,
    model_name: str = "cosyvoice-v3-flash",
    voice_rate: float = 1.0,
    voice_pitch: float = 1.0,
    voice_volume: float = 1.0,
    mission_id: str = None,
    segment_id: int = 1,
    segment_total: int = 1,
    instruction: str = "",
) -> float:
    mission_id = mission_id or str(uuid.uuid4())
    request_data = {
        "user_id": user_id,
        "token": token,
        "voice_id": voice_id,
        "model_name": model_name,
        "text": text,
        "mission_id": mission_id,
        "segment_id": segment_id,
        "segment_total": segment_total,
        "rate": normalize_cosyvoice_rate(voice_rate),
        "pitch": normalize_cosyvoice_rate(voice_pitch),
        "volume": convert_volume_to_percent(voice_volume),
    }
    if instruction:
        request_data["instruction"] = instruction

    response = requests.post(
        f"{domestic_interface}voice_clone/tts",
        data=request_data,
        timeout=TTS_REQUEST_TIMEOUT,
    )
    response.raise_for_status()
    response_data = response.json()
    _require_success(response_data, "生成CosyVoice配音失败")
    return _save_remote_tts_result(
        response_data=response_data,
        audio_file=audio_file,
        subtitle_file=subtitle_file,
        domestic_interface=domestic_interface,
        text=text,
        missing_audio_message="生成CosyVoice配音成功但没有返回音频地址",
    )


def cosyvoice_tts(
    text: str,
    voice_name: str,
    model_name: str,
    audio_file: str,
    subtitle_file: str,
    domestic_interface: str,
    user_id,
    token: str,
    voice_rate: float = 1.0,
    voice_pitch: float = 1.0,
    voice_volume: float = 1.0,
    mission_id: str = None,
    segment_id: int = 1,
    segment_total: int = 1,
    instruction: str = "",
) -> float:
    mission_id = mission_id or str(uuid.uuid4())
    request_data = {
        "user_id": user_id,
        "token": token,
        "text": text,
        "voice_name": voice_name,
        "model_name": model_name,
        "mission_id": mission_id,
        "segment_id": segment_id,
        "segment_total": segment_total,
        "rate": normalize_cosyvoice_rate(voice_rate),
        "pitch": normalize_cosyvoice_rate(voice_pitch),
        "volume": convert_volume_to_percent(voice_volume),
    }
    if instruction:
        request_data["instruction"] = instruction

    response = requests.post(
        f"{domestic_interface}cosy_voice",
        data=request_data,
        timeout=TTS_REQUEST_TIMEOUT,
    )
    print(f'response---{response}')
    response.raise_for_status()
    response_data = response.json()
    _require_success(response_data, "生成CosyVoice配音失败")
    return _save_remote_tts_result(
        response_data=response_data,
        audio_file=audio_file,
        subtitle_file=subtitle_file,
        domestic_interface=domestic_interface,
        text=text,
        missing_audio_message="生成CosyVoice配音成功但没有返回音频地址",
    )


def index_tts_clone_tts(
    text: str,
    audio_url: str,
    audio_file: str,
    subtitle_file: str,
    domestic_interface: str,
    user_id,
    token: str,
    mission_id: str = None,
    segment_id: int = 1,
    segment_total: int = 1,
) -> float:
    if not audio_url:
        raise ValueError("未配置 IndexTTS 参考音频地址")

    mission_id = mission_id or str(uuid.uuid4())
    response = requests.post(
        f"{domestic_interface}index_tts",
        data={
            "user_id": user_id,
            "token": token,
            "audio_url": audio_url,
            "text": text or "",
            "mission_id": mission_id,
            "segment_id": segment_id,
            "segment_total": segment_total,
        },
        timeout=TTS_REQUEST_TIMEOUT,
    )
    response.raise_for_status()
    response_data = response.json()
    _require_success(response_data, "生成 IndexTTS 配音失败")
    return _save_remote_tts_result(
        response_data=response_data,
        audio_file=audio_file,
        subtitle_file=subtitle_file,
        domestic_interface=domestic_interface,
        text=text or "",
        missing_audio_message="生成 IndexTTS 配音成功但没有返回音频地址",
    )