"""Kimi (Moonshot) adapter using OpenAI SDK."""

import base64
import logging
import mimetypes
import time
from pathlib import Path
from typing import List, Optional

from openai import OpenAI

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

_logger = logging.getLogger(__name__)


class KimiAdapter(VendorAdapter):
    """
    Moonshot Kimi adapter using OpenAI SDK.

    Supports:
    - File content extraction via Files API (PDF, DOC, etc.)
    - Vision models with base64 image input
    - Custom base_url for proxies
    - Configurable file cleanup and wait timeout
    """

    # Default endpoint and model
    DEFAULT_BASE_URL = "https://api.moonshot.cn/v1"
    DEFAULT_MODEL = "moonshot-v1-auto"

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

        Args:
            api_key: Moonshot API key.
            model: Model name. Defaults to moonshot-v1-auto.
            base_url: Custom API endpoint.
            timeout: Request timeout in seconds.
            file_wait_timeout: Max seconds to wait for file processing. Default 60.
            cleanup_files: Whether to delete uploaded files after chat. Default False.
            **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.file_wait_timeout = file_wait_timeout
        self.cleanup_files = cleanup_files

        self.client = OpenAI(
            api_key=self.api_key,
            base_url=self.base_url,
            timeout=self.timeout
        )

    def upload_file(self, file_path: Path) -> str:
        """
        Upload file to Kimi Files API.

        Returns:
            File ID for use in content extraction.
        """
        path = self._validate_file(file_path)

        with open(path, "rb") as f:
            uploaded = self.client.files.create(file=f, purpose="file-extract")

        # Wait for server processing (time-based, not count-based)
        start_time = time.time()
        while True:
            file_info = self.client.files.retrieve(file_id=uploaded.id)
            if file_info.status == "ok":
                break
            elapsed = time.time() - start_time
            if elapsed >= self.file_wait_timeout:
                raise RuntimeError(
                    f"Kimi file processing timeout after {self.file_wait_timeout}s "
                    f"(file_id={uploaded.id}). Consider increasing file_wait_timeout parameter."
                )
            time.sleep(2)

        return uploaded.id

    def delete_file(self, file_id: str) -> None:
        """Delete an uploaded file from Kimi Files API."""
        try:
            self.client.files.delete(file_id=file_id)
            _logger.debug(f"Successfully deleted Kimi file: {file_id}")
        except Exception as e:
            # Log but don't interrupt flow - cleanup failure is non-critical
            _logger.warning(f"Failed to cleanup Kimi file {file_id}: {e}")

    def extract_file_content(self, file_id: str) -> str:
        """
        Extract text content from an uploaded file.

        Uses Kimi's files.content API to retrieve the extracted text,
        which can then be passed to the model as context.

        Args:
            file_id: File ID from upload_file().

        Returns:
            Extracted text content of the file.
        """
        content = self.client.files.content(file_id=file_id)
        return content.text

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

        The following models support vision:
        - kimi-latest, kimi-k2*, moonshot-v1-auto (new generation general models)
        - moonshot-v1-vision-preview (legacy vision-specific model)
        """
        m = self.model.lower()
        return (
            "vision" in m           # moonshot-v1-vision-preview
            or m == "kimi-latest"
            or m.startswith("kimi-k")  # kimi-k2, kimi-k2.5 etc.
            or m == "moonshot-v1-auto"
        )

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

        File handling strategy:
        1. Image files -> base64 vision input (requires vision-capable model)
        2. PDF / Document files -> text extraction into system message (Moonshot recommended)
           If extraction is empty, pass through to model for natural response

        Args:
            prompt: User prompt text.
            files: Optional list of file paths.
            **kwargs: Additional parameters (temperature, max_tokens, etc.)

        Returns:
            Response with content, usage, and raw response.
        """
        messages = []
        vision_parts: List[dict] = []
        uploaded_file_ids: List[str] = []  # Track for cleanup

        try:
            if files:
                is_vision = self._is_vision_capable()

                for file_path in files:
                    path = self._validate_file(Path(file_path))
                    self._check_file_size(path)  # Prevent OOM on large files
                    mime_type = mimetypes.guess_type(str(path))[0] or "application/octet-stream"

                    if mime_type.startswith("image/"):
                        # Image files: direct base64 encoding
                        if not is_vision:
                            raise ValueError(
                                f"Model '{self.model}' does not support image input.\n"
                                "Please use a vision-capable model, e.g., model='kimi-latest'"
                            )
                        with open(path, "rb") as f:
                            b64_data = base64.b64encode(f.read()).decode("utf-8")
                        vision_parts.append({
                            "type": "image_url",
                            "image_url": {"url": f"data:{mime_type};base64,{b64_data}"}
                        })

                    else:
                        # Document files: text extraction -> system message
                        file_id = self.upload_file(path)
                        uploaded_file_ids.append(file_id)
                        file_text = self.extract_file_content(file_id)
                        if file_text and file_text.strip():
                            messages.append({"role": "system", "content": file_text})
                            _logger.debug(f"Kimi file extraction succeeded: {path.name} ({len(file_text)} chars)")
                        else:
                            # Empty extraction - log warning and continue
                            _logger.warning(
                                f"Kimi file extraction returned empty content for: {path.name}. "
                                f"The model will respond without document context."
                            )

                # Build user message
                if vision_parts:
                    vision_parts.append({"type": "text", "text": prompt})
                    messages.append({"role": "user", "content": vision_parts})
                else:
                    messages.append({"role": "user", "content": prompt})

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

            # Build request params - let API use its own defaults
            request_kwargs = {
                "model": self.model,
                "messages": messages,
            }
            if "temperature" in kwargs:
                request_kwargs["temperature"] = kwargs.pop("temperature")
            if "max_tokens" in kwargs:
                request_kwargs["max_tokens"] = kwargs.pop("max_tokens")
            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)
            content = completion.choices[0].message.content or ""

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

        finally:
            # Cleanup uploaded files if requested
            if self.cleanup_files:
                for file_id in uploaded_file_ids:
                    self.delete_file(file_id)
