import os
import json
import time
from typing import List, Dict, Optional
from pathlib import Path
from filelock import FileLock

from .schema import LLMModel

# 默认内置模型列表
# 价格单位：人民币/百万tokens
DEFAULT_MODELS = [
    {
        "name": "volcengine/deepseek-v3-1-terminus",
        "description": "火山引擎 DeepSeek V3.1 模型，适合编程任务",
        "model_name": "deepseek-v3-1-terminus",
        "model_type": "saas/openai",
        "base_url": "https://ark.cn-beijing.volces.com/api/v3",
        "provider": "volcengine",
        "api_key_path": "",
        "is_reasoning": False,
        "input_price": 4.0,  # 4元/百万tokens (缓存未命中)
        "output_price": 12.0,  # 12元/百万tokens
        "max_output_tokens": 8096,
        "context_window": 128000,
    },
    {
        "name": "volcengine/deepseek-v3-2",
        "description": "火山引擎 DeepSeek V3.2 模型，适合编程任务",
        "model_name": "deepseek-v3-2-251201",
        "model_type": "saas/openai",
        "base_url": "https://ark.cn-beijing.volces.com/api/v3",
        "provider": "volcengine",
        "api_key_path": "",
        "is_reasoning": True,
        "input_price": 2.0,
        "output_price": 3.0,
        "max_output_tokens": 8096,
        "context_window": 128000,
    },
    {
        "name": "volcengine/doubao-seed-1-6-thinking",
        "description": "火山引擎 豆包 Seed-1-6-Thinking 模型，适合编程任务",
        "model_name": "doubao-seed-1-6-thinking-250715",
        "model_type": "saas/openai",
        "base_url": "https://ark.cn-beijing.volces.com/api/v3",
        "provider": "volcengine",
        "api_key_path": "",
        "is_reasoning": True,
        "input_price": 0.8,
        "output_price": 8.0,
        "max_output_tokens": 32000,
        "context_window": 256000,
    },
    {
        "name": "bigmodel/glm-5",
        "description": "智谱 AI GLM-5，国产大模型，性价比高",
        "model_name": "glm-5",
        "model_type": "saas/openai",
        "base_url": "https://open.bigmodel.cn/api/paas/v4/",
        "provider": "bigmodel",
        "api_key_path": "",
        "is_reasoning": False,
        "input_price": 4.0,  # 0.8元/百万tokens
        "output_price": 16.0,  # 2.0元/百万tokens
        "max_output_tokens": 100000,
        "context_window": 200000,
    },
    {
        "name": "bigmodel/coding/glm-5",
        "description": "智谱 AI GLM-5，国产大模型，性价比高,用于coding plan 订阅",
        "model_name": "glm-5",
        "model_type": "saas/openai",
        "base_url": "https://open.bigmodel.cn/api/coding/paas/v4",
        "provider": "bigmodel",
        "api_key_path": "",
        "is_reasoning": False,
        "input_price": 0.0,
        "output_price": 0.0,
        "max_output_tokens": 100000,
        "context_window": 200000,
    },
    {
        "name": "minimax/m2_5",
        "description": "MiniMax M2.5 模型，适合编程任务",
        "model_name": "MiniMax-M2.5",
        "model_type": "saas/openai",
        "base_url": "https://api.minimaxi.com/v1",
        "provider": "minimax",
        "is_reasoning": True,
        "input_price": 2.1,
        "output_price": 8.4,
        "max_output_tokens": 64000,
        "context_window": 204800,
        "api_key_path": "",
    },
    {
        "name": "minimax/coding/m2_5",
        "description": "MiniMax M2.5 模型，用于coding plan 订阅",
        "model_name": "MiniMax-M2.5",
        "model_type": "saas/openai",
        "base_url": "https://api.minimaxi.com/v1",
        "provider": "minimax",
        "is_reasoning": True,
        "input_price": 0.0,
        "output_price": 0.0,
        "max_output_tokens": 64000,
        "context_window": 204800,
        "api_key_path": "",
    },
    {
        "name": "deepseek/deepseek-chat",
        "description": "DeepSeek 官方 API，性能接近 GPT-4，价格低廉",
        "model_name": "deepseek-chat",
        "model_type": "saas/openai",
        "base_url": "https://api.deepseek.com/beta",
        "provider": "deepseek",
        "api_key_path": "",
        "is_reasoning": False,
        "input_price": 2.0,
        "output_price": 3.0,
        "max_output_tokens": 8096,
        "context_window": 128000,
    },
    {
        "name": "deepseek/deepseek-reasoner",
        "description": "DeepSeek 官方 API，推理能力更强，价格低廉",
        "model_name": "deepseek-reasoner",
        "model_type": "saas/openai",
        "base_url": "https://api.deepseek.com/beta",
        "provider": "deepseek",
        "api_key_path": "",
        "is_reasoning": True,
        "input_price": 2.0,
        "output_price": 3.0,
        "max_output_tokens": 64000,
        "context_window": 128000,
        "thinking": {"type": "enabled"},
    },
    {
        "name": "deepseek/deepseek-reasoner-v3.2-speciale",
        "description": "DeepSeek 官方 API，推理能力更强，价格低廉",
        "model_name": "deepseek-reasoner",
        "model_type": "saas/openai",
        "base_url": "https://api.deepseek.com/v3.2_speciale_expires_on_20251215",
        "provider": "deepseek",
        "api_key_path": "",
        "is_reasoning": True,
        "input_price": 2.0,
        "output_price": 3.0,
        "max_output_tokens": 64000,
        "context_window": 128000,
        "thinking": {"type": "enabled"},
    },
    {
        "name": "openrouter/claude-sonnet-4-6",
        "description": "Claude Sonnet 4.6 via OpenRouter，强大的推理能力",
        "model_name": "anthropic/claude-sonnet-4.6",
        "model_type": "saas/openai",
        "base_url": "https://openrouter.ai/api/v1",
        "provider": "openrouter",
        "api_key_path": "",
        "is_reasoning": False,
        "input_price": 21.6,
        "output_price": 108.0,
        "max_output_tokens": 128000,
        "context_window": 1000000,
    },
    {
        "name": "openrouter/claude-opus-4-6",
        "description": "Claude Opus 4.6 via OpenRouter，Anthropic 最强编程模型，适合长流程 Agent 任务",
        "model_name": "anthropic/claude-opus-4.6",
        "model_type": "saas/openai",
        "base_url": "https://openrouter.ai/api/v1",
        "provider": "openrouter",
        "api_key_path": "",
        "is_reasoning": True,
        "input_price": 36.0,  # $5/百万tokens ≈ 36元
        "output_price": 180.0,  # $25/百万tokens ≈ 180元
        "max_output_tokens": 128000,
        "context_window": 1000000,
    },
    {
        "name": "openrouter/openai/gpt-5",
        "description": "GPT-5 via OpenRouter，OpenAI 最新模型",
        "model_name": "openai/gpt-5.2",
        "model_type": "saas/openrouter",
        "base_url": "https://openrouter.ai/api/v1",
        "provider": "openrouter",
        "api_key_path": "",
        "is_reasoning": True,
        "input_price": 9.0,  # 1.25美元/百万tokens ≈ 9元
        "output_price": 72.0,  # 10美元/百万tokens ≈ 72元
        "max_output_tokens": 120000,
        "context_window": 400000,
        "thinking": {"type": "enabled"},
    },
    {
        "name": "openrouter/openai/gpt-5-pro",
        "description": "GPT-5 Pro via OpenRouter，OpenAI 最新模型",
        "model_name": "openai/gpt-5.2-pro",
        "model_type": "saas/openrouter",
        "base_url": "https://openrouter.ai/api/v1",
        "provider": "openrouter",
        "api_key_path": "",
        "is_reasoning": True,
        "input_price": 105.0,
        "output_price": 840.0,
        "max_output_tokens": 12000,
        "context_window": 400000,
        "thinking": {"type": "enabled"},
    },
    {
        "name": "moonshot/kimi-k2.5",
        "description": "Moonshot Kimi K2.5",
        "model_name": "kimi-k2.5",
        "model_type": "saas/openai",
        "base_url": "https://api.moonshot.cn/v1",
        "provider": "moonshot",
        "is_reasoning": True,
        "input_price": 4.0,
        "output_price": 21.0,
        "max_output_tokens": 32000,
        "context_window": 262000,
    },
    {
        "name": "siliconflow/kimi-k2.5",
        "description": "Moonshot Kimi K2.5 via SiliconFlow",
        "model_name": "Pro/moonshotai/Kimi-K2.5",
        "model_type": "saas/openai",
        "base_url": "https://api.siliconflow.cn/v1",
        "provider": "siliconflow",
        "is_reasoning": True,
        "input_price": 4.0,
        "output_price": 21.0,
        "max_output_tokens": 32000,
        "context_window": 262000,
    },
    {
        "name": "siliconflow/deepseek-v3.2",
        "description": "DeepSeek V3.2 via SiliconFlow",
        "model_name": "Pro/deepseek-ai/DeepSeek-V3.2",
        "model_type": "saas/openai",
        "base_url": "https://api.siliconflow.cn/v1",
        "provider": "siliconflow",
        "is_reasoning": True,
        "input_price": 2.0,
        "output_price": 3.0,
        "max_output_tokens": 32000,
        "context_window": 160000,
    },
    {
        "name": "siliconflow/deepseek-v3.1-terminus",
        "description": "DeepSeek V3.1-Terminus via SiliconFlow",
        "model_name": "Pro/deepseek-ai/DeepSeek-V3.1-Terminus",
        "model_type": "saas/openai",
        "base_url": "https://api.siliconflow.cn/v1",
        "provider": "siliconflow",
        "is_reasoning": True,
        "input_price": 4.0,
        "output_price": 12.0,
        "max_output_tokens": 32000,
        "context_window": 160000,
    },
    {
        "name": "siliconflow/glm-5",
        "description": "GLM-5 via SiliconFlow",
        "model_name": "Pro/zai-org/GLM-5",
        "model_type": "saas/openai",
        "base_url": "https://api.siliconflow.cn/v1",
        "provider": "siliconflow",
        "is_reasoning": True,
        "input_price": 18.0,
        "output_price": 22.0,
        "max_output_tokens": 32000,
        "context_window": 200000,
    },
    {
        "name": "siliconflow/minimax-m2.5",
        "description": "MiniMax M2.5 via SiliconFlow",
        "model_name": "Pro/MiniMaxAI/MiniMax-M2.5",
        "model_type": "saas/openai",
        "base_url": "https://api.siliconflow.cn/v1",
        "provider": "siliconflow",
        "is_reasoning": True,
        "input_price": 2.1,
        "output_price": 8.4,
        "max_output_tokens": 64000,
        "context_window": 200000,
        "api_key_path": "",
    },
]


class ModelRegistry:
    """模型注册表，负责模型的持久化和加载（无缓存）"""

    def __init__(self, models_json_path: Optional[str] = None):
        self.models_json_path = Path(
            models_json_path or os.path.expanduser("~/.auto-coder/keys/models.json")
        )

    def _ensure_dir(self):
        """确保目录存在"""
        self.models_json_path.parent.mkdir(parents=True, exist_ok=True)

    def _get_lock_path(self, file_path: Path) -> str:
        """获取锁文件路径"""
        return str(file_path) + ".lock"

    def _load_api_key_from_file(self, model: LLMModel) -> LLMModel:
        """从文件加载 API 密钥"""
        # 设置 base_keys_dir 为配置文件所在目录
        model.base_keys_dir = str(self.models_json_path.parent)

        # 如果 api_key 存在但 api_key_path 不存在，生成 api_key_path
        if model.api_key and not model.api_key_path:
            model.api_key_path = model.name.replace("/", "_")

        # 如果 api_key_path 存在，以它为准读取文件
        if model.api_key_path:
            api_key_file = self.models_json_path.parent / model.api_key_path
            if api_key_file.exists():
                with FileLock(self._get_lock_path(api_key_file), timeout=5):
                    model.api_key = api_key_file.read_text(encoding="utf-8").strip()
        return model

    def load(self) -> List[LLMModel]:
        """每次都重新加载模型列表，合并默认模型和自定义模型"""
        self._ensure_dir()

        # 从默认模型开始
        models_dict = {}
        for model_data in DEFAULT_MODELS:
            # 添加 base_keys_dir 到模型数据
            model_data_with_keys_dir = model_data.copy()
            model_data_with_keys_dir["base_keys_dir"] = str(
                self.models_json_path.parent
            )
            model = LLMModel(**model_data_with_keys_dir)
            models_dict[model.name] = model

        # 如果配置文件存在，读取并合并
        if self.models_json_path.exists():
            try:
                with FileLock(self._get_lock_path(self.models_json_path), timeout=5):
                    with open(self.models_json_path, "r", encoding="utf-8") as f:
                        custom_models_data = json.load(f)

                # 自定义模型会覆盖同名的默认模型
                for model_data in custom_models_data:
                    # 兼容旧版本：添加新字段的默认值
                    if "context_window" not in model_data:
                        model_data["context_window"] = 128 * 1024
                    if "max_output_tokens" not in model_data:
                        model_data["max_output_tokens"] = 8096
                    if "provider" not in model_data:
                        model_data["provider"] = None

                    # 添加 base_keys_dir
                    model_data["base_keys_dir"] = str(self.models_json_path.parent)

                    model = LLMModel(**model_data)
                    models_dict[model.name] = model

            except (json.JSONDecodeError, Exception) as e:
                # JSON 无效时，使用默认模型并重新保存
                print(f"警告：加载 models.json 失败: {e}，使用默认模型")
                self.save(list(models_dict.values()))
        else:
            # 文件不存在，创建默认配置
            self.save(list(models_dict.values()))

        # 加载 API 密钥
        models = []
        for model in models_dict.values():
            models.append(self._load_api_key_from_file(model))

        return models

    def save(self, models: List[LLMModel]) -> None:
        """使用 filelock 保存模型列表到文件"""
        self._ensure_dir()

        # 转换为字典列表，不包含 api_key
        models_data = [model.dict() for model in models]

        # 使用 filelock 写入，超时5秒
        lock_path = self._get_lock_path(self.models_json_path)
        with FileLock(lock_path, timeout=5):
            with open(self.models_json_path, "w", encoding="utf-8") as f:
                json.dump(models_data, f, indent=2, ensure_ascii=False)

    def get(self, name: str) -> Optional[LLMModel]:
        """根据名称获取模型（每次重新加载）"""
        models = self.load()
        for model in models:
            if model.name == name.strip():
                return model
        return None

    def get_all(self) -> Dict[str, LLMModel]:
        """获取所有模型（每次重新加载）"""
        models = self.load()
        return {model.name: model for model in models}

    def add_or_update(self, model: LLMModel) -> None:
        """添加或更新模型"""
        # 先加载所有模型
        models = self.load()

        # 查找是否存在同名模型
        updated = False
        for i, existing_model in enumerate(models):
            if existing_model.name == model.name:
                models[i] = model
                updated = True
                break

        # 如果没有找到同名模型，添加新模型
        if not updated:
            models.append(model)

        # 保存更新后的模型列表
        self.save(models)

    def save_api_key(self, model_name: str, api_key: str) -> bool:
        """保存 API 密钥到文件"""
        model = self.get(model_name)
        if not model:
            return False

        # 确保有 api_key_path
        if not model.api_key_path:
            model.api_key_path = model_name.replace("/", "_")

        # 保存密钥到文件
        api_key_file = self.models_json_path.parent / model.api_key_path

        # 使用 filelock 保存密钥，超时5秒
        lock_path = self._get_lock_path(api_key_file)
        with FileLock(lock_path, timeout=5):
            api_key_file.write_text(api_key.strip(), encoding="utf-8")

        # 更新模型（清除内存中的 api_key）
        model.api_key = None
        self.add_or_update(model)

        return True

    def remove_model(self, model_name: str) -> bool:
        """
        删除模型

        Args:
            model_name: 要删除的模型名称

        Returns:
            bool: 是否成功删除
        """
        # 先检查模型是否存在
        model = self.get(model_name)
        if not model:
            return False

        # 如果是默认模型，不允许删除
        default_model_names = [m["name"] for m in DEFAULT_MODELS]
        if model_name in default_model_names:
            return False

        # 加载所有模型
        models = self.load()

        # 过滤掉要删除的模型
        filtered_models = [m for m in models if m.name != model_name]

        # 如果模型数量没有变化，说明模型不存在于自定义模型中
        if len(filtered_models) == len(models):
            return False

        # 删除 API 密钥文件（如果存在）
        if model.api_key_path:
            api_key_file = self.models_json_path.parent / model.api_key_path
            if api_key_file.exists():
                try:
                    # 使用 filelock 删除密钥文件
                    lock_path = self._get_lock_path(api_key_file)
                    with FileLock(lock_path, timeout=5):
                        api_key_file.unlink()
                except OSError:
                    # 忽略删除密钥文件失败的情况
                    pass

        # 保存更新后的模型列表
        self.save(filtered_models)

        return True
