"""
api.v1.providers_api — LLM provider management (wraps ~/.agentpaas/providers.json).

GET    /api/v1/providers               List all providers + configured status
POST   /api/v1/providers/{id}          Save/update provider API key
DELETE /api/v1/providers/{id}          Remove provider API key
POST   /api/v1/providers/{id}/test     Test provider connectivity
"""
from __future__ import annotations
import json
import os
from typing import Optional

from fastapi import APIRouter, Depends
from pydantic import BaseModel

from agentpaas.api.deps import get_tenant
from agentpaas.api.middleware.auth import TenantContext

router = APIRouter(prefix="/providers", tags=["providers"])

PROVIDERS_FILE = os.path.expanduser("~/.agentpaas/providers.json")

DEFAULT_PROVIDERS: dict = {
    "claude-code": {
        "name": "Claude Code (Max Plan)",
        "type": "claude-code",
        "base_url": "",
        "models": ["sonnet", "opus", "haiku"],
        "env_key": "",
        "no_key_required": True,
    },
    "anthropic": {
        "name": "Anthropic (Claude API)",
        "type": "anthropic",
        "base_url": "https://api.anthropic.com",
        "models": ["claude-sonnet-4-6", "claude-opus-4-7", "claude-haiku-4-5-20251001"],
        "env_key": "ANTHROPIC_API_KEY",
    },
    "dashscope": {
        "name": "阿里云 DashScope（通义千问）",
        "type": "openai_compatible",
        "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
        "models": ["qwen3-max-2026-01-23", "qwen-max", "qwen-plus", "qwen-turbo"],
        "env_key": "DASHSCOPE_API_KEY",
    },
    "deepseek": {
        "name": "DeepSeek",
        "type": "openai_compatible",
        "base_url": "https://api.deepseek.com/v1",
        "models": ["deepseek-chat", "deepseek-reasoner"],
        "env_key": "DEEPSEEK_API_KEY",
    },
    "openai": {
        "name": "OpenAI",
        "type": "openai",
        "base_url": "https://api.openai.com/v1",
        "models": ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo"],
        "env_key": "OPENAI_API_KEY",
    },
    "zhipu": {
        "name": "智谱 AI (GLM)",
        "type": "openai_compatible",
        "base_url": "https://open.bigmodel.cn/api/paas/v4",
        "models": ["glm-4-plus", "glm-4-flash"],
        "env_key": "ZHIPU_API_KEY",
    },
    "moonshot": {
        "name": "Moonshot (Kimi)",
        "type": "openai_compatible",
        "base_url": "https://api.moonshot.cn/v1",
        "models": ["moonshot-v1-8k", "moonshot-v1-32k"],
        "env_key": "MOONSHOT_API_KEY",
    },
    "ollama": {
        "name": "Ollama（本地模型）",
        "type": "ollama",
        "base_url": "http://localhost:11434",
        "models": ["qwen2.5:7b", "llama3.2:3b", "mistral:7b"],
        "env_key": "",
        "no_key_required": True,
    },
}


def _load_providers() -> dict:
    if os.path.exists(PROVIDERS_FILE):
        with open(PROVIDERS_FILE) as f:
            return json.load(f)
    return {}


def _save_providers(p: dict) -> None:
    os.makedirs(os.path.dirname(PROVIDERS_FILE), exist_ok=True)
    with open(PROVIDERS_FILE, "w") as f:
        json.dump(p, f, indent=2)


def _is_configured(pid: str, saved: dict) -> bool:
    p = DEFAULT_PROVIDERS.get(pid, {})
    if p.get("no_key_required"):
        return True
    entry = saved.get(pid, {})
    # Check saved file or env var
    env_key = p.get("env_key", "")
    return bool(entry.get("api_key") or (env_key and os.getenv(env_key)))


def _live_ollama_models(base_url: str = "http://127.0.0.1:11434") -> list:
    """探测本地 Ollama 实际安装的模型（2s 超时；失败返回 []）。"""
    try:
        import urllib.request
        with urllib.request.urlopen(f"{base_url}/api/tags", timeout=2) as r:
            data = json.loads(r.read())
        return [m["name"] for m in data.get("models", [])]
    except Exception:
        return []


@router.get("")
async def list_providers(_tenant: TenantContext = Depends(get_tenant)):
    saved = _load_providers()
    providers = []
    for pid, p in DEFAULT_PROVIDERS.items():
        entry = saved.get(pid, {})
        models = p["models"]
        if pid == "ollama":
            # 模型切换下拉要列真实可用的本地模型，而非静态示例
            live = _live_ollama_models()
            if live:
                models = live
        providers.append({
            "id": pid,
            "name": p["name"],
            "type": p["type"],
            "base_url": entry.get("base_url") or p["base_url"],
            "models": models,
            "env_key": p.get("env_key", ""),
            "no_key_required": p.get("no_key_required", False),
            "configured": _is_configured(pid, saved),
        })
    return {"providers": providers}


@router.get("/models-in-use")
async def models_in_use(tenant: TenantContext = Depends(get_tenant)):
    """各智能体当前使用的大模型一览（模型页「显示与切换」的数据源）。

    从每个活跃智能体当前版本的 config.model 解析 provider/name。
    """
    from agentpaas.db.session import get_db
    db = get_db()
    rows = db.fetchall(
        "SELECT a.id, a.name, a.current_version, v.config "
        "FROM agents a JOIN agent_versions v "
        "  ON v.agent_id = a.id AND v.version = a.current_version "
        "WHERE a.tenant_id = ? AND a.status = 'active' "
        "ORDER BY a.updated_at DESC",
        (tenant.tenant_id,),
    )
    items = []
    for r in rows:
        try:
            cfg = json.loads(r["config"] or "{}")
        except Exception:
            cfg = {}
        model = cfg.get("model") or {}
        if isinstance(model, str):  # 容错: model 可能是 "provider/name" 字符串
            parts = model.split("/", 1)
            model = {"provider": parts[0] if len(parts) > 1 else "",
                     "name": parts[-1]}
        items.append({
            "agent_id": r["id"],
            "agent_name": r["name"],
            "version": r["current_version"],
            "provider": model.get("provider", "") or "",
            "model": model.get("name", "") or "",
        })
    return {"items": items, "count": len(items)}


class SaveProviderRequest(BaseModel):
    api_key: Optional[str] = None
    base_url: Optional[str] = None


@router.post("/{provider_id}")
async def save_provider(
    provider_id: str,
    req: SaveProviderRequest,
    _tenant: TenantContext = Depends(get_tenant),
):
    if provider_id not in DEFAULT_PROVIDERS:
        from fastapi import HTTPException
        raise HTTPException(404, {"error": {"code": "PROVIDER_NOT_FOUND"}})
    saved = _load_providers()
    entry = saved.get(provider_id, {})
    if req.api_key is not None:
        entry["api_key"] = req.api_key
        # Also set the env variable for the current process so lambdagent picks it up
        env_key = DEFAULT_PROVIDERS[provider_id].get("env_key", "")
        if env_key:
            os.environ[env_key] = req.api_key
    if req.base_url is not None:
        entry["base_url"] = req.base_url
    saved[provider_id] = entry
    _save_providers(saved)
    return {"ok": True}


@router.delete("/{provider_id}")
async def remove_provider(
    provider_id: str,
    _tenant: TenantContext = Depends(get_tenant),
):
    saved = _load_providers()
    saved.pop(provider_id, None)
    _save_providers(saved)
    return {"ok": True}


@router.post("/{provider_id}/test")
async def test_provider(
    provider_id: str,
    _tenant: TenantContext = Depends(get_tenant),
):
    """Send a minimal request to verify the provider is reachable."""
    if provider_id not in DEFAULT_PROVIDERS:
        from fastapi import HTTPException
        raise HTTPException(404, {"error": {"code": "PROVIDER_NOT_FOUND"}})

    p = DEFAULT_PROVIDERS[provider_id]
    saved = _load_providers()
    entry = saved.get(provider_id, {})

    if p.get("no_key_required") and p["type"] == "claude-code":
        # Just verify claude CLI is available
        import shutil
        if shutil.which("claude"):
            return {"ok": True, "message": "Claude Code CLI 已找到"}
        return {"ok": False, "message": "未找到 claude 命令，请先安装 Claude Code"}

    if p.get("no_key_required") and p["type"] == "ollama":
        import urllib.request
        base = entry.get("base_url") or p["base_url"]
        try:
            urllib.request.urlopen(f"{base}/api/tags", timeout=3)
            return {"ok": True, "message": f"Ollama 服务正常 ({base})"}
        except Exception as e:
            return {"ok": False, "message": f"无法连接 Ollama: {e}"}

    # For API-key providers, try a minimal list-models or health call
    api_key = entry.get("api_key") or os.getenv(p.get("env_key", ""), "")
    if not api_key:
        return {"ok": False, "message": "未配置 API Key"}

    base_url = entry.get("base_url") or p["base_url"]
    try:
        import urllib.request as ur
        req_obj = ur.Request(
            f"{base_url}/models",
            headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
        )
        ur.urlopen(req_obj, timeout=5)
        return {"ok": True, "message": "连接成功"}
    except Exception as e:
        # A 401/403 still means the endpoint is reachable
        err = str(e)
        if "401" in err or "403" in err:
            return {"ok": False, "message": "API Key 无效或权限不足"}
        return {"ok": True, "message": f"服务可达（{err[:60]}）"}
