import logging
from typing import Dict, Any, Optional

import httpx

from src.config.settings import MINIMAX_GROUP_ID
from src.services.provider.base import BaseMusicService, ServiceResponse, TaskStatus, GenerationResult

logger = logging.getLogger(__name__)


class MiniMaxMusicService(BaseMusicService):
    BASE_URL = "https://api.minimaxi.com/v1"

    def __init__(self, model_name: str = "music-2.5", api_key: Optional[str] = None, **kwargs):
        super().__init__(model_name, api_key=api_key, **kwargs)
        if not self.api_key:
            logger.warning("API Key is not set. MiniMax music generation will fail.")

    def _build_headers(self, group_id: Optional[str] = None) -> Dict[str, str]:
        headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {self.api_key}",
        }
        gid = group_id or MINIMAX_GROUP_ID
        if gid:
            headers["x-minimax-group-id"] = gid
        return headers

    async def _request_json_async(self, method: str, url: str, headers: Dict[str, str], timeout: tuple = (30, 600), **kwargs) -> Dict[str, Any]:
        """发送HTTP请求，默认连接30秒，读取600秒（10分钟）；音乐生成耗时常需较长读取时间"""
        async with httpx.AsyncClient(timeout=timeout) as client:
            response = await client.request(method, url, headers=headers, **kwargs)
        if response.status_code != 200:
            logger.error("MiniMax request failed: %s - %s", response.status_code, response.text)
            response.raise_for_status()
        return response.json()

    @staticmethod
    def _ensure_success(data: Dict[str, Any], context: str) -> None:
        base_resp = data.get("base_resp", {})
        status_code = base_resp.get("status_code")
        if status_code is not None and int(status_code) != 0:
            status_msg = base_resp.get("status_msg", "unknown error")
            # 提供更友好的错误提示
            if int(status_code) == 2013 and "sensitive" in status_msg.lower():
                raise RuntimeError(
                    f"MiniMax {context} failed: 内容包含敏感词汇，请修改后重试"
                )
            raise RuntimeError(f"MiniMax {context} failed: {status_code} {status_msg}")

    async def _generate_lyrics(
        self,
        prompt: str,
        title: Optional[str] = None,
        mode: str = "write_full_song",
        lyrics: Optional[str] = None,
        group_id: Optional[str] = None,
    ) -> Dict[str, Any]:
        headers = self._build_headers(group_id=group_id)
        payload: Dict[str, Any] = {
            "mode": mode,
            "prompt": prompt,
        }
        if title:
            payload["title"] = title
        if mode == "edit" and lyrics:
            payload["lyrics"] = lyrics

        url = f"{self.BASE_URL}/lyrics_generation"
        data = await self._request_json_async("POST", url, headers, json=payload)
        self._ensure_success(data, "lyrics generation")
        return data

    async def generate_lyrics(
        self,
        prompt: str,
        mode: str = "write_full_song",
        lyrics: Optional[str] = None,
        title: Optional[str] = None,
        **kwargs
    ) -> Dict[str, Any]:
        """Public lyrics generation API used by backend controller."""
        return await self._generate_lyrics(
            prompt=prompt,
            title=title,
            mode=mode,
            lyrics=lyrics,
            group_id=kwargs.get("group_id"),
        )

    async def generate(self, lyrics: str, prompt: Optional[str] = None, **kwargs) -> ServiceResponse:
        """
        Generate music with MiniMax /v1/music_generation.

        Behavior:
        - If lyrics are absent, optionally calls /v1/lyrics_generation first.
        - Uses output_format=url by default for direct playback/download URLs.
        """
        headers = self._build_headers(group_id=kwargs.get("group_id"))

        final_lyrics = (lyrics or "").strip()
        lyrics_meta: Optional[Dict[str, Any]] = None

        if not final_lyrics:
            lyrics_prompt = (kwargs.get("lyrics_prompt") or kwargs.get("lyricsPrompt") or prompt or "").strip()
            if not lyrics_prompt:
                raise RuntimeError("lyrics is required unless lyrics_prompt or prompt is provided for auto lyrics generation")

            lyrics_resp = await self._generate_lyrics(
                prompt=lyrics_prompt,
                title=kwargs.get("title"),
                mode=kwargs.get("lyrics_mode", "write_full_song"),
                lyrics=kwargs.get("seed_lyrics"),
                group_id=kwargs.get("group_id"),
            )
            final_lyrics = (lyrics_resp.get("lyrics") or "").strip()
            if not final_lyrics:
                raise RuntimeError(f"MiniMax lyrics generation returned empty lyrics: {lyrics_resp}")

            lyrics_meta = {
                "song_title": lyrics_resp.get("song_title"),
                "style_tags": lyrics_resp.get("style_tags"),
            }

        output_format = str(kwargs.get("output_format") or kwargs.get("outputFormat") or "url").lower()
        if output_format not in {"url", "hex"}:
            output_format = "url"

        payload: Dict[str, Any] = {
            "model": self.model_name,
            "lyrics": final_lyrics,
            "output_format": output_format,
            "stream": bool(kwargs.get("stream", False)),
            "aigc_watermark": bool(kwargs.get("aigc_watermark", kwargs.get("aigcWatermark", False))),
        }

        if prompt is not None:
            payload["prompt"] = prompt

        audio_setting = kwargs.get("audio_setting") or kwargs.get("audioSetting")
        if isinstance(audio_setting, dict) and audio_setting:
            payload["audio_setting"] = audio_setting

        if payload["stream"] and payload["output_format"] != "hex":
            payload["output_format"] = "hex"

        url = f"{self.BASE_URL}/music_generation"
        data = await self._request_json_async("POST", url, headers, json=payload)
        self._ensure_success(data, "music generation")

        music_data = data.get("data", {}) if isinstance(data.get("data"), dict) else {}
        status_code = int(music_data.get("status") or 2)
        audio_value = music_data.get("audio")

        if status_code != 2:
            # MiniMax 文档: status 1=合成中 2=已完成。当前接口为同步调用，若返回 1 表示服务端排队/繁忙，
            # 且不支持通过 trace_id 轮询结果，故直接失败并提示用户稍后重试，避免任务“成功但无 URL”。
            if status_code == 1:
                logger.warning("MiniMax music_generation returned status=1 (合成中), no polling supported")
                raise RuntimeError(
                    "音乐生成排队中或服务器繁忙，请稍后重试。若多次出现可尝试缩短歌词或更换描述。"
                )
            logger.warning("MiniMax music_generation returned status=%s, full data keys: %s", status_code, list(data.keys()))
            raise RuntimeError(f"音乐生成返回异常状态({status_code})，请稍后重试。")

        result = GenerationResult()
        if output_format == "url" and audio_value:
            result.url = str(audio_value)
        elif audio_value:
            result.content = str(audio_value)

        if not result.url and not result.content:
            raise RuntimeError(f"MiniMax music generation returned no audio payload: {data}")

        return ServiceResponse(
            status=TaskStatus.SUCCEEDED,
            task_id=str(data.get("trace_id") or data.get("id") or "minimax-music"),
            results=[result],
            meta={
                "provider_response": data,
                "lyrics_meta": lyrics_meta,
            }
        )

    async def check_status(self, task_id: str) -> ServiceResponse:
        """
        MiniMax music_generation is synchronous under current integration.
        """
        return ServiceResponse(
            status=TaskStatus.UNKNOWN,
            task_id=task_id,
            error="MiniMax music generation is synchronous in this integration."
        )
