"""
Vidu API Provider
=================

封装 Vidu 语音/视频相关 HTTP API：
- 同步 TTS（/ent/v2/audio-tts）
- 声音复刻（/ent/v2/audio-clone）
- 视频生成（/ent/v2/lip-sync）
- 查询任务（/ent/v2/tasks/{id}/creations）

统一使用 httpx.AsyncClient，由 lifespan 统一管理生命周期。
"""

from __future__ import annotations

import logging
from typing import Any

import httpx

from app.config import get_settings
from app.core.exceptions import PlatformError, PlatformErrorType

logger = logging.getLogger(__name__)


def _map_vidu_error(status: int, message: str) -> PlatformError:
    """把 Vidu HTTP 错误映射为标准 PlatformError"""
    mapping = {
        429: (PlatformErrorType.RATE_LIMIT, True),
        401: (PlatformErrorType.AUTH_FAILED, False),
        403: (PlatformErrorType.AUTH_FAILED, False),
        400: (PlatformErrorType.BAD_REQUEST, False),
        404: (PlatformErrorType.NOT_FOUND, False),
        500: (PlatformErrorType.SERVER_ERROR, True),
        502: (PlatformErrorType.SERVER_ERROR, True),
        503: (PlatformErrorType.SERVER_ERROR, True),
    }
    error_type, retryable = mapping.get(status, (PlatformErrorType.UNKNOWN, False))
    return PlatformError(
        message=message,
        platform="vidu",
        retryable=retryable,
        error_type=error_type,
        status_code=status,
    )


class ViduProvider:
    """Vidu API 客户端封装

    使用 httpx.AsyncClient，支持外部注入（由 lifespan 管理生命周期）。
    """

    def __init__(
        self,
        api_key: str | None = None,
        base_url: str | None = None,
        client: httpx.AsyncClient | None = None,
    ):
        settings = get_settings()
        self.api_key = api_key or settings.VIDU_API_KEY
        if base_url:
            self.base_url = base_url.rstrip("/")
        else:
            from app.core.platform_config import get_platform_config_loader

            platform_config = get_platform_config_loader().get_platform("vidu")
            self.base_url = (platform_config.base_url if platform_config else "https://api.vidu.cn").rstrip("/")

        if not self.api_key:
            raise ValueError("Vidu API Key 未配置，请在 .env 中设置 VIDU_API_KEY")

        if client is not None:
            self.client = client
            self._owns_client = False
            # 外部传入的 client 也要补认证头（main.py / scheduler 共用 client 场景）
            self.client.headers["Authorization"] = f"Token {self.api_key}"
            self.client.headers["Content-Type"] = "application/json"
        else:
            self.client = httpx.AsyncClient(
                timeout=httpx.Timeout(30.0, connect=5.0),
                limits=httpx.Limits(max_connections=20, max_keepalive_connections=20),
                headers={
                    "Authorization": f"Token {self.api_key}",
                    "Content-Type": "application/json",
                },
            )
            self._owns_client = True

    async def close(self) -> None:
        """关闭 HTTP Client，释放连接池。仅在自己创建 Client 时关闭。"""
        if self._owns_client and not self.client.is_closed:
            await self.client.aclose()

    # ==================== TTS 语音合成 ====================

    async def tts_sync(
        self,
        text: str,
        voice_id: str,
        speed: float = 1.0,
        volume: int = 0,
        pitch: int = 0,
        emotion: str | None = None,
        pronunciation_dict_tone: list[str] | None = None,
        payload: str | None = None,
    ) -> dict[str, Any]:
        """同步语音合成

        POST /ent/v2/audio-tts
        """
        url = f"{self.base_url}/ent/v2/audio-tts"

        body: dict[str, Any] = {
            "text": text,
            "voice_setting_voice_id": voice_id,
            "voice_setting_speed": speed,
            "voice_setting_volume": volume,
            "voice_setting_pitch": pitch,
        }
        if emotion:
            body["voice_setting_emotion"] = emotion
        if pronunciation_dict_tone:
            body["pronunciation_dict_tone"] = pronunciation_dict_tone
        if payload:
            body["payload"] = payload

        logger.info(f"[Vidu TTS] 请求参数: text_length={len(text)}")

        logger.info(f"[Vidu TTS] 提交请求: url={url}, body={body}")

        try:
            # 文本较长时同步合成可能耗时较久，超时时间放宽到 120 秒
            resp = await self.client.post(url, json=body, timeout=httpx.Timeout(120.0, connect=5.0))
            data = resp.json()
            if resp.status_code != 200 or data.get("state") == "failed":
                msg = data.get("err_code") or data.get("message") or f"HTTP {resp.status_code}"
                logger.error(f"[Vidu TTS] 请求失败: url={url}, status={resp.status_code}, response={data}")
                raise _map_vidu_error(resp.status_code, f"Vidu TTS error: {msg}")
            return data
        except (httpx.NetworkError, httpx.TimeoutException) as e:
            logger.error(f"[Vidu TTS] 网络错误: {e}")
            raise PlatformError(
                f"Vidu TTS 网络错误: {e}",
                platform="vidu",
                retryable=True,
                error_type=PlatformErrorType.TIMEOUT,
            ) from e

    # ==================== 声音复刻 ====================

    async def clone_voice(
        self,
        audio_url: str,
        voice_id: str,
        text: str,
        prompt_audio_url: str | None = None,
        prompt_text: str | None = None,
        payload: str | None = None,
    ) -> dict[str, Any]:
        """声音复刻（同步接口）

        POST /ent/v2/audio-clone
        """
        url = f"{self.base_url}/ent/v2/audio-clone"

        body: dict[str, Any] = {
            "audio_url": audio_url,
            "voice_id": voice_id,
            "text": text,
        }
        if prompt_audio_url:
            body["prompt_audio_url"] = prompt_audio_url
        if prompt_text:
            body["prompt_text"] = prompt_text
        if payload:
            body["payload"] = payload

        try:
            # 声音复刻处理音频可能耗时较久，超时时间放宽到 120 秒
            resp = await self.client.post(url, json=body, timeout=httpx.Timeout(120.0, connect=5.0))
            data = resp.json()
            if resp.status_code != 200 or data.get("state") == "failed":
                msg = data.get("err_code") or data.get("message") or f"HTTP {resp.status_code}"
                logger.error(f"[Vidu Clone] 请求失败: url={url}, status={resp.status_code}, response={data}")
                raise _map_vidu_error(resp.status_code, f"Vidu clone error: {msg}")
            return data
        except (httpx.NetworkError, httpx.TimeoutException) as e:
            logger.error(f"[Vidu Clone] 网络错误: {e}")
            raise PlatformError(
                f"Vidu Clone 网络错误: {e}",
                platform="vidu",
                retryable=True,
                error_type=PlatformErrorType.TIMEOUT,
            ) from e

    # ==================== 视频生成 ====================

    async def lip_sync(
        self,
        video_url: str,
        audio_url: str | None = None,
        text: str | None = None,
        voice_id: str | None = None,
        speed: float = 1.0,
        volume: int = 0,
        ref_photo_url: str | None = None,
        callback_url: str | None = None,
        payload: str | None = None,
    ) -> dict[str, Any]:
        """视频生成（异步接口）

        POST /ent/v2/lip-sync
        """
        url = f"{self.base_url}/ent/v2/lip-sync"

        body: dict[str, Any] = {"video_url": video_url}

        if audio_url:
            body["audio_url"] = audio_url
        if text:
            body["text"] = text
        if voice_id:
            body["voice_id"] = voice_id
        if speed != 1.0:
            body["speed"] = speed
        if volume != 0:
            body["volume"] = volume
        if ref_photo_url:
            body["ref_photo_url"] = ref_photo_url
        if callback_url:
            body["callback_url"] = callback_url
        if payload:
            body["payload"] = payload

        try:
            resp = await self.client.post(url, json=body)
            data = resp.json()
            if resp.status_code != 200 or data.get("state") == "failed":
                msg = data.get("err_code") or data.get("message") or f"HTTP {resp.status_code}"
                logger.error(f"[Vidu LipSync] 请求失败: url={url}, status={resp.status_code}, response={data}")
                raise _map_vidu_error(resp.status_code, f"Vidu lip-sync error: {msg}")
            return data
        except (httpx.NetworkError, httpx.TimeoutException) as e:
            logger.error(f"[Vidu LipSync] 网络错误: {e}")
            raise PlatformError(
                f"Vidu LipSync 网络错误: {e}",
                platform="vidu",
                retryable=True,
                error_type=PlatformErrorType.TIMEOUT,
            ) from e

    # ==================== 查询任务 ====================

    async def query_task(self, task_id: str) -> dict[str, Any]:
        """查询任务状态及生成物

        GET /ent/v2/tasks/{task_id}/creations
        """
        url = f"{self.base_url}/ent/v2/tasks/{task_id}/creations"

        try:
            resp = await self.client.get(url)
            data = resp.json()
            if resp.status_code != 200:
                msg = data.get("err_code") or data.get("message") or f"HTTP {resp.status_code}"
                logger.error(f"[Vidu Query] 请求失败: url={url}, status={resp.status_code}, response={data}")
                raise _map_vidu_error(resp.status_code, f"Vidu query task error: {msg}")
            return data
        except (httpx.NetworkError, httpx.TimeoutException) as e:
            logger.error(f"[Vidu Query] 网络错误: {e}")
            raise PlatformError(
                f"Vidu Query 网络错误: {e}",
                platform="vidu",
                retryable=True,
                error_type=PlatformErrorType.TIMEOUT,
            ) from e
