import logging
from typing import Dict, Any, Optional

import httpx

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

logger = logging.getLogger(__name__)


class MiniMaxVideoService(BaseVideoService):
    BASE_URL = "https://api.minimaxi.com/v1"

    def __init__(self, model_name: str = "MiniMax-Hailuo-2.3", 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 video generation will fail.")
        self.base_url = self.BASE_URL

    def _build_headers(self, api_key: Optional[str] = None, group_id: Optional[str] = None, include_content_type: bool = True) -> Dict[str, str]:
        headers = {
            "Authorization": f"Bearer {api_key or self.api_key}",
        }
        if include_content_type:
            headers["Content-Type"] = "application/json"

        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], **kwargs) -> Dict[str, Any]:
        async with httpx.AsyncClient(timeout=httpx.Timeout(30.0, read=120.0)) as client:
            resp = await client.request(method, url, headers=headers, **kwargs)
        if resp.status_code != 200:
            logger.error(f"MiniMax request failed: {resp.status_code} - {resp.text}")
            resp.raise_for_status()
        return resp.json()

    @staticmethod
    def _ensure_success(data: Dict[str, Any], context: str) -> None:
        base_resp = data.get("base_resp")
        if isinstance(base_resp, dict):
            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")
                raise RuntimeError(f"MiniMax {context} failed: {status_code} {status_msg}")

    @staticmethod
    def _extract_task_id(data: Dict[str, Any]) -> Optional[str]:
        for key in ("task_id", "id"):
            value = data.get(key)
            if value:
                return str(value)
        payload = data.get("data")
        if isinstance(payload, dict):
            for key in ("task_id", "id"):
                value = payload.get(key)
                if value:
                    return str(value)
        return None

    async def generate(self, prompt: str, **kwargs) -> ServiceResponse:
        """
        Initiate MiniMax video generation task.
        Supports T2V and I2V based on parameters.
        """
        user_id = kwargs.get('user_id')
        key_config = self.get_effective_api_key_with_config(user_id)
        if not key_config or not key_config.get('api_key'):
            logger.error(f"No API key available for {self._provider_id}")
            return ServiceResponse(
                status=TaskStatus.FAILED,
                error="API key not configured"
            )

        api_key = key_config['api_key']
        extra_config = key_config.get('extra_config', {})

        # Get group_id from extra_config or kwargs
        group_id = extra_config.get('group_id') or kwargs.get("group_id")

        url = f"{self.base_url}/video_generation"

        headers = self._build_headers(api_key, group_id, include_content_type=True)

        # Use provided model name or default to class init model
        api_model = self.model_name

        payload = {
            "model": api_model,
            "prompt": prompt,
            "aigc_watermark": bool(kwargs.get("aigc_watermark", False)),
            "prompt_optimizer": bool(kwargs.get("prompt_optimizer", False)),
        }

        # Optional image inputs: first image is first frame, second image is last frame.
        # Keep single source of truth from normalized `image_inputs`.
        image_inputs = kwargs.get("image_inputs") or []
        if isinstance(image_inputs, str):
            image_inputs = [image_inputs]

        first_frame = image_inputs[0] if len(image_inputs) > 0 else None
        last_frame = image_inputs[1] if len(image_inputs) > 1 else None
        if first_frame:
            payload["first_frame_image"] = first_frame
        if last_frame:
            payload["last_frame_image"] = last_frame

        # MiniMax only supports 6s and 10s durations
        duration = kwargs.get("duration")
        if duration:
            duration = int(duration)
            # 映射 unsupported durations to nearest supported value
            if duration <= 6:
                duration = 6
            else:
                duration = 10
            payload["duration"] = duration

        if kwargs.get("resolution"):
            payload["resolution"] = str(kwargs["resolution"]).upper()

        if kwargs.get("fast_pretreatment") is not None:
            payload["fast_pretreatment"] = kwargs["fast_pretreatment"]

        if kwargs.get("callback_url"):
            payload["callback_url"] = kwargs["callback_url"]

        try:
            logger.info(
                "MiniMax video create task: model=%s, duration=%s, resolution=%s, has_first_frame=%s, has_last_frame=%s",
                api_model,
                payload.get("duration"),
                payload.get("resolution"),
                bool(payload.get("first_frame_image")),
                bool(payload.get("last_frame_image")),
            )

            data = await self._request_json_async("POST", url, headers, json=payload)
            self._ensure_success(data, "create task")

            task_id = self._extract_task_id(data)
            if not task_id:
                raise RuntimeError(f"No task_id returned from MiniMax: {data}")

            return ServiceResponse(
                task_id=str(task_id),
                status=TaskStatus.QUEUED,
                meta={"provider": "minimax", "model": api_model}
            )

        except Exception as e:
            logger.error(f"MiniMax Video Generation Failed: {str(e)}")
            raise RuntimeError(f"MiniMax Video Service Error: {str(e)}")

    async def check_status(self, task_id: str, **kwargs) -> ServiceResponse:
        """
        Check status of video generation task.
        """
        # Get keys from kwargs or use system defaults
        api_key = kwargs.get('api_key') or self.api_key
        group_id = kwargs.get('group_id')

        url = f"{self.base_url}/query/video_generation"
        params = {"task_id": task_id}
        headers = self._build_headers(api_key, group_id, include_content_type=False)

        try:
            data = await self._request_json_async("GET", url, headers, params=params)
            self._ensure_success(data, "query task")

            # Common response: {"task_id": "...", "status": "Success", "file_id": "...", ...}
            status_str = data.get("status", "").lower()

            status_map = {
                "preparing": TaskStatus.QUEUED,
                "queueing": TaskStatus.QUEUED,
                "queued": TaskStatus.QUEUED,
                "processing": TaskStatus.PROCESSING,
                "running": TaskStatus.PROCESSING,
                "success": TaskStatus.SUCCEEDED,
                "succeeded": TaskStatus.SUCCEEDED,
                "completed": TaskStatus.SUCCEEDED,
                "fail": TaskStatus.FAILED,
                "failed": TaskStatus.FAILED,
                "error": TaskStatus.FAILED,
            }

            status = status_map.get(status_str, TaskStatus.UNKNOWN)

            result = None
            if status == TaskStatus.SUCCEEDED:
                file_id = data.get("file_id")
                video_url = None

                # If we have file_id, fetch actual download URL via files/retrieve
                if file_id:
                    video_url = await self._get_file_url(file_id, api_key, group_id)

                # Fallback from status response fields
                if not video_url:
                    video_url = (
                        data.get("video_url")
                        or data.get("download_url")
                        or (data.get("file", {}) or {}).get("download_url")
                    )

                if video_url:
                    result = [GenerationResult(
                        url=video_url,
                        content=str(file_id) if file_id else None,
                    )]

            elif status == TaskStatus.FAILED:
                # Try multiple fields for error message
                error_msg = (
                    data.get("error_message")
                    or data.get("message")
                    or (data.get("base_resp", {}) or {}).get("status_msg")
                    or "Unknown error"
                )
                return ServiceResponse(task_id=task_id, status=status, error=str(error_msg))

            return ServiceResponse(
                task_id=task_id,
                status=status,
                results=result,
                meta=data
            )

        except Exception as e:
            logger.error(f"MiniMax Video Status Check Failed: {str(e)}")
            return ServiceResponse(
                task_id=task_id,
                status=TaskStatus.UNKNOWN,
                error=str(e)
            )

    async def _get_file_url(self, file_id: str, api_key: Optional[str] = None, group_id: Optional[str] = None) -> Optional[str]:
        try:
            url = f"{self.base_url}/files/retrieve"
            params = {"file_id": file_id}
            headers = self._build_headers(api_key, group_id, include_content_type=False)

            data = await self._request_json_async("GET", url, headers, params=params)
            self._ensure_success(data, "retrieve file")
            # Common response: {"file": {"download_url": "..."}}
            file_payload = data.get("file") if isinstance(data.get("file"), dict) else {}
            return file_payload.get("download_url") or data.get("download_url")
        except Exception as e:
            logger.error(f"Failed to retrieve file URL for {file_id}: {e}")
            return None
