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

import base64
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


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
    """
    
    # 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,
        **kwargs
    ):
        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.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")

        # 等待服务端处理完成（status 变为 ok），最多等 60 秒
        for _ in range(30):
            file_info = self.client.files.retrieve(file_id=uploaded.id)
            if file_info.status == "ok":
                break
            time.sleep(2)
        else:
            raise RuntimeError(f"Kimi 文件处理超时（file_id={uploaded.id}）")

        return uploaded.id
    
    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_model(self) -> bool:
        """Check if current model is a vision model."""
        return "vision" in self.model.lower()
    
    def chat(
        self,
        prompt: str,
        files: Optional[List[Path]] = None,
        **kwargs
    ) -> Response:
        """
        Send chat request to Kimi.
        
        For vision models: sends images as base64 content (OpenAI vision format).
        For document files: uploads, extracts text content, passes as system context.
        """
        messages = []
        
        if files and self._is_vision_model():
            # Vision model: separate images from non-image files
            image_parts = []
            non_image_paths = []
            
            for file_path in files:
                path = self._validate_file(Path(file_path))
                mime_type = mimetypes.guess_type(str(path))[0] or "application/octet-stream"
                if mime_type.startswith("image/"):
                    with open(path, "rb") as f:
                        b64_data = base64.b64encode(f.read()).decode("utf-8")
                    image_parts.append({
                        "type": "image_url",
                        "image_url": {"url": f"data:{mime_type};base64,{b64_data}"}
                    })
                else:
                    non_image_paths.append(path)
            
            # 上传非图片文件，使用 fileid:// 协议引用
            file_refs = []
            for path in non_image_paths:
                file_id = self.upload_file(path)
                file_refs.append(f"fileid://{file_id}")

            if image_parts:
                # 有图片：混合格式（图片 base64 + 文本 prompt + 文件引用）
                text_content = prompt
                if file_refs:
                    text_content += "\n\n参考文件：" + "\n".join(file_refs)
                image_parts.append({"type": "text", "text": text_content})
                messages.append({"role": "user", "content": image_parts})
            else:
                # 无图片：纯文本格式（prompt + 文件引用）
                text_content = prompt
                if file_refs:
                    text_content += "\n\n参考文件：" + "\n".join(file_refs)
                messages.append({"role": "user", "content": text_content})
        
        elif files:
            # Non-vision model: upload files and reference using fileid:// protocol
            # Kimi 官方文档推荐用 fileid:// 在消息中直接引用文件
            file_references = []
            for file_path in files:
                path = self._validate_file(Path(file_path))
                file_id = self.upload_file(path)
                file_references.append(f"fileid://{file_id}")
            # 把 prompt 和文件引用合并到一条消息
            content_with_files = prompt + "\n\n参考文件：" + "\n".join(file_references)
            messages.append({"role": "user", "content": content_with_files})
        
        else:
            # Pure text
            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
        )
