"""Xiaomi MiMo adapter using OpenAI SDK."""

import base64
import mimetypes
import warnings
from pathlib import Path
from typing import List, Optional, Union

from openai import OpenAI

from .base import VendorAdapter
from ..models import Response, Usage


class MimoAdapter(VendorAdapter):
    """
    Xiaomi MiMo adapter using OpenAI SDK.

    MiMo API is fully compatible with OpenAI format.

    Supports:
    - Vision models with base64 image input or URL
    - Audio/Video input via URL or base64
    - Custom base_url for proxies
    - Deep thinking mode (reasoning_content in response)

    Models:
    - mimo-v2-pro: Flagship model with thinking enabled by default
    - mimo-v2-omni: Multimodal model (vision, audio, video)
    - mimo-v2-tts: Text-to-speech model
    - mimo-v2-flash: Fast model with thinking disabled by default

    File handling:
    - MiMo does NOT have a dedicated Files API
    - Images: base64 or public URL
    - Audio/Video: base64 or public URL (with fps/resolution params)
    """

    # Default endpoint and model
    DEFAULT_BASE_URL = "https://api.xiaomimimo.com/v1"
    DEFAULT_MODEL = "mimo-v2-flash"

    # Precise model capability sets (avoid string matching false positives)
    VISION_MODELS = {"mimo-v2-pro", "mimo-v2-omni", "mimo-v2-flash"}
    MULTIMODAL_MODELS = {"mimo-v2-omni"}  # Only omni supports audio/video

    def __init__(
        self,
        api_key: str,
        model: Optional[str] = None,
        base_url: Optional[str] = None,
        timeout: Optional[float] = None,
        validate_urls: bool = False,
        **kwargs
    ):
        """
        Initialize MiMo adapter.

        Args:
            api_key: Xiaomi MiMo API key.
            model: Model name. Defaults to mimo-v2-flash.
            base_url: Custom API endpoint.
            timeout: Request timeout in seconds.
            validate_urls: Whether to validate URL accessibility before sending.
                          Default False (validation adds latency).
            **kwargs: Additional options.
        """
        super().__init__(
            api_key=api_key,
            model=model or self.DEFAULT_MODEL,
            base_url=base_url or self.DEFAULT_BASE_URL,
            timeout=timeout,
            **kwargs
        )

        self.validate_urls = validate_urls

        # MiMo accepts both auth styles:
        # - api-key header (custom)
        # - Authorization: Bearer (OpenAI SDK default)
        self.client = OpenAI(
            api_key=self.api_key,
            base_url=self.base_url,
            timeout=self.timeout
        )

    def upload_file(self, file_path: Path) -> str:
        """
        MiMo does not have a dedicated Files API.

        For images/audio/video, use base64 encoding or provide a public URL.
        For documents, include content directly in the prompt.

        Raises:
            NotImplementedError: MiMo does not support file uploads.
        """
        raise NotImplementedError(
            "MiMo does not support a dedicated Files API. "
            "For images/audio/video, they will be automatically encoded as base64 in chat(). "
            "Alternatively, you can pass a public URL string in the files parameter. "
            "For documents, please include the content directly in your prompt."
        )

    def _is_vision_capable(self) -> bool:
        """
        Check if current model supports vision (image input).

        Only mimo-v2-pro, mimo-v2-omni, mimo-v2-flash support vision.
        mimo-v2-tts is TTS-only and does NOT support images.
        """
        return self.model.lower() in self.VISION_MODELS

    def _is_multimodal_capable(self) -> bool:
        """
        Check if current model supports full multimodal (audio/video).

        Only mimo-v2-omni supports audio and video input.
        """
        return self.model.lower() in self.MULTIMODAL_MODELS

    def _validate_url(self, url: str) -> bool:
        """
        Validate if URL is accessible via HEAD request.

        Args:
            url: URL to validate.

        Returns:
            True if URL returns 2xx/3xx status, False otherwise.
        """
        try:
            import urllib.request
            import urllib.error

            req = urllib.request.Request(url, method="HEAD")
            req.add_header("User-Agent", "Mozilla/5.0 AIB-MiMo-Adapter")

            with urllib.request.urlopen(req, timeout=5) as response:
                return 200 <= response.status < 400
        except Exception:
            return False

    def _build_media_content(
        self,
        file_path: Optional[Path] = None,
        url: Optional[str] = None,
        media_type: str = "image",
        fps: Optional[float] = None,
        media_resolution: Optional[str] = None
    ) -> dict:
        """
        Build a media content part for the message.

        Args:
            file_path: Local file path (will be base64 encoded)
            url: Public URL to the media file
            media_type: "image", "audio", or "video"
            fps: Frames per second for video (default 2, range 0.1-10)
            media_resolution: "default" or "max" for video quality

        Returns:
            Content part dict with type and URL
        """
        if url:
            # Use URL directly
            media_url = url
        elif file_path:
            # Base64 encode local file
            path = self._validate_file(file_path)
            self._check_file_size(path)  # Prevent OOM on large files
            mime_type = mimetypes.guess_type(str(path))[0] or "application/octet-stream"
            with open(path, "rb") as f:
                b64_data = base64.b64encode(f.read()).decode("utf-8")
            media_url = f"data:{mime_type};base64,{b64_data}"
        else:
            raise ValueError("Either file_path or url must be provided")

        # Build content part based on media type
        if media_type == "image":
            return {
                "type": "image_url",
                "image_url": {"url": media_url}
            }
        elif media_type == "video":
            part = {
                "type": "video_url",
                "video_url": {"url": media_url}
            }
            if fps is not None:
                part["fps"] = fps
            if media_resolution is not None:
                part["media_resolution"] = media_resolution
            return part
        elif media_type == "audio":
            part = {
                "type": "audio_url",
                "audio_url": {"url": media_url}
            }
            if media_resolution is not None:
                part["media_resolution"] = media_resolution
            return part
        else:
            raise ValueError(f"Unsupported media type: {media_type}")

    def _detect_media_type_from_url(self, url: str) -> Optional[str]:
        """
        Detect media type from URL path extension.

        Parses only the URL path component to avoid false positives from
        directory names or query parameters containing extension-like strings.

        Returns:
            "image", "video", "audio", or None if unknown.
        """
        from urllib.parse import urlparse

        try:
            parsed = urlparse(url)
            suffix = Path(parsed.path).suffix.lower()
        except Exception:
            suffix = ""

        IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"}
        VIDEO_EXTS = {".mp4", ".avi", ".mov", ".mkv", ".webm", ".flv", ".wmv"}
        AUDIO_EXTS = {".mp3", ".wav", ".m4a", ".flac", ".ogg", ".aac", ".wma"}

        if suffix in IMAGE_EXTS:
            return "image"
        if suffix in VIDEO_EXTS:
            return "video"
        if suffix in AUDIO_EXTS:
            return "audio"
        return None

    def chat(
        self,
        prompt: str,
        files: Optional[List[Union[str, Path]]] = None,
        fps: Optional[float] = None,
        media_resolution: Optional[str] = None,
        **kwargs
    ) -> Response:
        """
        Send chat request to MiMo.

        Args:
            prompt: User prompt text.
            files: Optional list of file paths or URLs.
                   - Local paths: will be base64 encoded
                   - URLs (http/https): used directly
                   Supported: images, audio, video (mimo-v2-omni only for audio/video)
            fps: Frames per second for video (default 2, range 0.1-10).
                 Higher = more temporal detail, more tokens.
            media_resolution: "default" (balanced) or "max" (best quality).
            **kwargs: Additional parameters (temperature, thinking, etc.)

        MiMo-specific kwargs:
        - thinking: {"type": "enabled"} or {"type": "disabled"} - control reasoning mode

        Example:
            # With local file
            response = bridge.chat("Describe this image", files=["photo.jpg"])

            # With URL
            response = bridge.chat("Analyze this video",
                                   files=["https://example.com/video.mp4"],
                                   fps=2, media_resolution="max")
        """
        messages = []
        content_parts: List[dict] = []

        if files:
            is_vision = self._is_vision_capable()
            is_multimodal = self._is_multimodal_capable()

            for file_item in files:
                # Check if it's a URL or local path
                if isinstance(file_item, str) and file_item.startswith(("http://", "https://")):
                    # It's a URL
                    url = file_item

                    # Optional URL validation
                    if self.validate_urls and not self._validate_url(url):
                        warnings.warn(
                            f"URL may not be accessible: {url}. "
                            "Request may fail. Disable validate_urls to skip this check."
                        )

                    # Detect media type from URL
                    media_type = self._detect_media_type_from_url(url)

                    if media_type is None:
                        # Unknown extension - warn user instead of silent default
                        warnings.warn(
                            f"Cannot determine media type from URL extension: {url}. "
                            "Treating as image. If this fails, ensure URL has a clear extension "
                            "(e.g., .jpg, .mp4, .mp3) or use local file path."
                        )
                        media_type = "image"

                    # Validate model capability
                    if media_type == "image" and not is_vision:
                        raise ValueError(f"Model '{self.model}' does not support image input.")
                    if media_type in ("video", "audio") and not is_multimodal:
                        raise ValueError(
                            f"Model '{self.model}' does not support {media_type} input. "
                            "Please use mimo-v2-omni."
                        )

                    content_parts.append(self._build_media_content(
                        url=url,
                        media_type=media_type,
                        fps=fps,
                        media_resolution=media_resolution
                    ))

                else:
                    # It's a local file path
                    path = self._validate_file(Path(file_item))
                    mime_type = mimetypes.guess_type(str(path))[0] or "application/octet-stream"

                    if mime_type.startswith("image/"):
                        if not is_vision:
                            raise ValueError(
                                f"Model '{self.model}' does not support image input."
                            )
                        content_parts.append(self._build_media_content(
                            file_path=path,
                            media_type="image"
                        ))

                    elif mime_type.startswith("video/"):
                        if not is_multimodal:
                            raise ValueError(
                                f"Model '{self.model}' does not support video input. "
                                "Please use mimo-v2-omni."
                            )
                        content_parts.append(self._build_media_content(
                            file_path=path,
                            media_type="video",
                            fps=fps,
                            media_resolution=media_resolution
                        ))

                    elif mime_type.startswith("audio/"):
                        if not is_multimodal:
                            raise ValueError(
                                f"Model '{self.model}' does not support audio input. "
                                "Please use mimo-v2-omni."
                            )
                        content_parts.append(self._build_media_content(
                            file_path=path,
                            media_type="audio",
                            media_resolution=media_resolution
                        ))

                    else:
                        raise ValueError(
                            f"MiMo does not support file type '{mime_type}'. "
                            "Only image, audio, and video files are supported. "
                            "For documents, please include content directly in your prompt."
                        )

            # Build user message with text + media
            content_parts.append({"type": "text", "text": prompt})
            messages.append({"role": "user", "content": content_parts})

        else:
            # Text only
            messages.append({"role": "user", "content": prompt})

        # Build request params
        request_kwargs = {
            "model": self.model,
            "messages": messages,
        }

        # Pass through common parameters
        if "temperature" in kwargs:
            request_kwargs["temperature"] = kwargs.pop("temperature")
        if "max_tokens" in kwargs:
            request_kwargs["max_tokens"] = kwargs.pop("max_tokens")
        if "max_completion_tokens" in kwargs:
            request_kwargs["max_completion_tokens"] = kwargs.pop("max_completion_tokens")
        if "top_p" in kwargs:
            request_kwargs["top_p"] = kwargs.pop("top_p")
        if "stop" in kwargs:
            request_kwargs["stop"] = kwargs.pop("stop")
        if "stream" in kwargs:
            request_kwargs["stream"] = kwargs.pop("stream")

        # MiMo-specific: thinking mode
        if "thinking" in kwargs:
            request_kwargs["thinking"] = kwargs.pop("thinking")

        # Any remaining kwargs
        request_kwargs.update(kwargs)

        # Send request
        completion = self.client.chat.completions.create(**request_kwargs)

        # Validate response
        if not completion.choices:
            return Response(
                content="",
                usage=Usage.from_openai(completion.usage),
                raw=completion
            )

        # Handle None content (e.g., when using tool_calls)
        message = completion.choices[0].message
        content = message.content or ""

        return Response(
            content=content,
            usage=Usage.from_openai(completion.usage),
            raw=completion
        )
