"""AI 场景配置管理 — 精简版（4 个端点）"""

import json
import logging
import time
import os
from typing import Optional

from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel
import httpx

from app.core.config import (
    settings, AI_SCENE_KEYS, get_ai_scene_config,
    _encrypt_value, _decrypt_value,
)
from app.core.database import get_db, SQLHelper, get_system_db, _has_split_databases

# Windows 编码兼容：确保 PYTHONUTF8 模式生效
if os.name == "nt":
    os.environ.setdefault("PYTHONUTF8", "1")
    os.environ.setdefault("PYTHONIOENCODING", "utf-8")

logger = logging.getLogger(__name__)
router = APIRouter()


# ── Request/Response Models ──

class SceneConfigUpdate(BaseModel):
    provider: str = "siliconflow"
    api_base: str = ""
    api_key: str = ""
    model: str = ""
    enabled: bool = True


class TestResult(BaseModel):
    success: bool
    model_name: str = ""
    latency_ms: int = 0
    error: str = ""
    error_code: str = ""
    warning: str = ""


# ── Provider 预设 ──

PROVIDER_PRESETS = {
    "siliconflow": {
        "name": "SiliconFlow（硅基流动）",
        "api_base": "https://api.siliconflow.cn/v1",
        "api_key_required": True,
        "description": "云端 API，支持 DeepSeek/BGE 等模型",
    },
    "openai_compatible": {
        "name": "OpenAI 兼容",
        "api_base": "",
        "api_key_required": True,
        "description": "任何 OpenAI 协议兼容的 API 服务",
    },
}


# ── Endpoints ──

@router.get("/vision-status")
async def get_vision_status():
    """检查视觉模型是否已配置且可用

    前端根据此接口决定是否允许上传图片/扫描版 PDF。
    返回 {"available": true/false, "model": "模型名称或空"}
    """
    _db_gen = get_system_db() if _has_split_databases() else get_db()
    async for db in _db_gen:
        config = await get_ai_scene_config("vision_model", db)
        available = (
            config.get("enabled", False)
            and bool(config.get("api_base"))
            and bool(config.get("api_key_decrypted") or config.get("api_key"))
            and bool(config.get("model"))
        )
        return {
            "available": available,
            "model": config.get("model", "") if available else "",
        }
    return {"available": False, "model": ""}


@router.get("/scenes")
async def get_scenes():
    """获取 4 个 AI 场景的当前配置"""
    scenes = {}
    _db_gen = get_system_db() if _has_split_databases() else get_db()
    async for db in _db_gen:
        for key in AI_SCENE_KEYS:
            config = await get_ai_scene_config(key, db)
            # 脱敏 API Key
            safe_config = {k: v for k, v in config.items() if k != "api_key_decrypted"}
            if safe_config.get("api_key"):
                raw_key = safe_config["api_key"]
                # 如果是加密的，尝试解密后脱敏
                try:
                    decrypted = _decrypt_value(raw_key)
                    safe_config["api_key_display"] = decrypted[:8] + "••••••" if len(decrypted) > 8 else "••••••"
                except Exception:
                    safe_config["api_key_display"] = raw_key[:8] + "••••••" if len(raw_key) > 8 else "••••••"
            else:
                safe_config["api_key_display"] = ""
            scenes[key] = safe_config
    return {"success": True, "data": scenes}


@router.put("/scenes/{scene_key}")
async def update_scene(scene_key: str, body: SceneConfigUpdate):
    """更新指定场景的配置"""
    if scene_key not in AI_SCENE_KEYS:
        raise HTTPException(status_code=400, detail=f"无效的场景 key: {scene_key}，合法值: {AI_SCENE_KEYS}")

    config_data = {
        "provider": body.provider,
        "api_base": body.api_base,
        "api_key": _encrypt_value(body.api_key) if body.api_key else "",
        "model": body.model,
        "enabled": body.enabled,
    }

    _db_gen = get_system_db() if _has_split_databases() else get_db()
    async for db in _db_gen:
        conn, cur = db
        config_json = json.dumps(config_data, ensure_ascii=False)

        # SQLite 和 MySQL 的 UPSERT 参数数量不同，需分别处理
        from app.core.database import _is_sqlite
        if _is_sqlite():
            await cur.execute(
                SQLHelper.upsert(
                    "system_configs",
                    "config_key, config_value, config_type, category, description",
                    "%s, %s, %s, %s, %s",
                    "config_value = %s, updated_at = NOW()"
                ),
                (f"ai_scene_{scene_key}", config_json, 'json', 'ai', f"AI场景配置-{scene_key}"),
            )
        else:
            await cur.execute(
                SQLHelper.upsert(
                    "system_configs",
                    "config_key, config_value, config_type, category, description",
                    "%s, %s, %s, %s, %s",
                    "config_value = %s, updated_at = NOW()"
                ),
                (f"ai_scene_{scene_key}", config_json, 'json', 'ai', f"AI场景配置-{scene_key}", config_json),
            )
        await conn.commit()

    # 清除 model_resolver 缓存
    try:
        from app.core.model_resolver import clear_model_config_cache
        clear_model_config_cache()
    except Exception:
        pass

    # 清除 agent engine 缓存
    try:
        from app.agent.engine import clear_engine_cache
        clear_engine_cache()
    except Exception:
        pass

    return {"success": True, "data": {"scene_key": scene_key, "updated": True}}


@router.post("/scenes/{scene_key}/test")
async def test_scene(scene_key: str):
    """连通性测试 — 发送最小请求验证可用性"""
    if scene_key not in AI_SCENE_KEYS:
        raise HTTPException(status_code=400, detail=f"无效的场景 key: {scene_key}")

    # 读取当前配置
    config = None
    _db_gen = get_system_db() if _has_split_databases() else get_db()
    async for db in _db_gen:
        config = await get_ai_scene_config(scene_key, db)
    if not config:
        return TestResult(success=False, error="未找到配置", error_code="NO_CONFIG")

    api_base = config.get("api_base", "")
    api_key = config.get("api_key_decrypted", config.get("api_key", ""))
    model_name = config.get("model", "")
    provider = config.get("provider", "")

    if not api_base or not model_name:
        return TestResult(success=False, error="API Base URL 或模型名未配置", error_code="MISSING_CONFIG")

    start = time.time()
    try:
        # 显式设置 UTF-8 编码，避免 Windows 下 ASCII 编码错误
        async with httpx.AsyncClient(timeout=15.0) as client:
            headers = {"Content-Type": "application/json; charset=utf-8"}
            if api_key:
                headers["Authorization"] = f"Bearer {api_key}"

            if scene_key == "chat_model":
                resp = await client.post(
                    f"{api_base}/chat/completions",
                    headers=headers,
                    json={"model": model_name, "messages": [{"role": "user", "content": "hi"}], "max_tokens": 10},
                )
            elif scene_key == "vision_model":
                # Vision 模型连通性测试：发送标准 OpenAI vision 格式
                resp = await client.post(
                    f"{api_base}/chat/completions",
                    headers=headers,
                    json={
                        "model": model_name,
                        "messages": [{"role": "user", "content": [{"type": "text", "text": "hi"}]}],
                        "max_tokens": 10,
                    },
                )
            elif scene_key == "embedding_model":
                # 使用纯 ASCII 测试文本，避免中文编码问题
                resp = await client.post(
                    f"{api_base}/embeddings",
                    headers=headers,
                    json={"model": model_name, "input": "hello test"},
                )
            elif scene_key == "rerank_model":
                resp = await client.post(
                    f"{api_base}/rerank",
                    headers=headers,
                    json={"model": model_name, "query": "hello test", "documents": ["test document"]},
                )
            else:
                return TestResult(success=False, error="未知场景类型", error_code="UNKNOWN_SCENE")

        latency = int((time.time() - start) * 1000)

        # 显式解码响应，避免编码猜测导致 ascii 错误
        try:
            resp_text = resp.content.decode("utf-8", errors="replace")
        except Exception:
            resp_text = resp.text

        if resp.status_code == 200:
            return TestResult(success=True, model_name=model_name, latency_ms=latency)
        elif resp.status_code == 401:
            return TestResult(success=False, error="认证失败，API Key 无效", error_code="AUTH_FAILED", model_name=model_name)
        elif resp.status_code == 404:
            return TestResult(success=False, error=f"模型不存在: {model_name}", error_code="MODEL_NOT_FOUND", model_name=model_name)
        else:
            error_detail = resp_text[:200]
            if scene_key == "vision_model" and resp.status_code == 400:
                error_detail += "（提示：该模型可能不支持 OpenAI 兼容的 vision 格式，请确认模型支持图片输入）"
            return TestResult(success=False, error=f"HTTP {resp.status_code}: {error_detail}", error_code="HTTP_ERROR", model_name=model_name)

    except httpx.TimeoutException:
        return TestResult(success=False, error="连接超时（15秒）", error_code="TIMEOUT", model_name=model_name)
    except httpx.ConnectError:
        return TestResult(success=False, error=f"无法连接到 {api_base}", error_code="CONNECT_ERROR", model_name=model_name)
    except Exception as e:
        # 安全地转换异常信息，避免编码错误导致二次异常
        try:
            error_msg = str(e)[:200]
        except (UnicodeEncodeError, UnicodeDecodeError):
            error_msg = repr(e)[:200]
        return TestResult(success=False, error=error_msg, error_code="UNKNOWN_ERROR", model_name=model_name)


@router.get("/providers")
async def get_providers():
    """获取 Provider 预设列表"""
    return {"success": True, "data": PROVIDER_PRESETS}


@router.post("/test-defaults")
async def test_default_config():
    """默认配置连通性测试 — 测试云端（SiliconFlow）"""
    async def _test_one(scene_key, provider, api_base, api_key, model_name):
        """测试单个场景的连通性"""
        if not model_name:
            return TestResult(success=False, error="未配置模型名", error_code="NO_MODEL").model_dump()
        if not api_base:
            return TestResult(success=False, error="未配置 API 地址", error_code="NO_API_BASE").model_dump()

        start = time.time()
        try:
            async with httpx.AsyncClient(timeout=15.0) as client:
                headers = {"Content-Type": "application/json; charset=utf-8"}
                if api_key:
                    headers["Authorization"] = f"Bearer {api_key}"

                if scene_key in ("chat_model", "vision_model"):
                    resp = await client.post(
                        f"{api_base}/chat/completions",
                        headers=headers,
                        json={"model": model_name, "messages": [{"role": "user", "content": "hi"}], "max_tokens": 5},
                    )
                elif scene_key == "embedding_model":
                    resp = await client.post(
                        f"{api_base}/embeddings",
                        headers=headers,
                        json={"model": model_name, "input": "hello test"},
                    )
                elif scene_key == "rerank_model":
                    resp = await client.post(
                        f"{api_base}/rerank",
                        headers=headers,
                        json={"model": model_name, "query": "hello test", "documents": ["test document"]},
                    )

            latency = int((time.time() - start) * 1000)
            if resp.status_code == 200:
                return TestResult(success=True, model_name=model_name, latency_ms=latency).model_dump()
            elif resp.status_code == 404:
                return TestResult(success=False, error=f"模型不存在: {model_name}", error_code="MODEL_NOT_FOUND", model_name=model_name).model_dump()
            else:
                try:
                    resp_text = resp.content.decode("utf-8", errors="replace")[:100]
                except Exception:
                    resp_text = ""
                return TestResult(success=False, error=f"HTTP {resp.status_code}: {resp_text}", error_code="HTTP_ERROR", model_name=model_name).model_dump()

        except httpx.TimeoutException:
            return TestResult(success=False, error="连接超时（15秒）", error_code="TIMEOUT", model_name=model_name).model_dump()
        except httpx.ConnectError:
            return TestResult(success=False, error=f"无法连接到 {api_base}", error_code="CONNECT_ERROR", model_name=model_name).model_dump()
        except Exception as e:
            try:
                error_msg = str(e)[:200]
            except (UnicodeEncodeError, UnicodeDecodeError):
                error_msg = repr(e)[:200]
            return TestResult(success=False, error=error_msg, error_code="UNKNOWN_ERROR", model_name=model_name).model_dump()

    # 云端配置（SiliconFlow）
    cloud_scenes = {
        "chat_model": {"provider": "siliconflow", "api_base": settings.SILICONFLOW_API_BASE, "api_key": settings.SILICONFLOW_API_KEY, "model": getattr(settings, "SF_CHAT_MODEL", "Qwen/Qwen2.5-7B-Instruct")},
        "vision_model": {"provider": "siliconflow", "api_base": settings.VISION_API_BASE, "api_key": settings.VISION_API_KEY, "model": settings.VISION_MODEL},
        "embedding_model": {"provider": "siliconflow", "api_base": settings.SILICONFLOW_API_BASE, "api_key": settings.SILICONFLOW_API_KEY, "model": "BAAI/bge-m3"},
        "rerank_model": {"provider": "siliconflow", "api_base": settings.SILICONFLOW_API_BASE, "api_key": settings.SILICONFLOW_API_KEY, "model": "BAAI/bge-reranker-v2-m3"},
    }

    cloud_results = {}

    for scene_key, cfg in cloud_scenes.items():
        cloud_results[scene_key] = await _test_one(scene_key, model_name=cfg["model"], **{k:v for k,v in cfg.items() if k != "model"})

    return {"success": True, "data": {"cloud": cloud_results}}


# ── 启动时自动初始化 ai_scene_* 配置 ──

async def init_ai_scene_defaults():
    """检测并初始化 system_configs 中的 ai_scene_* 配置（如不存在则插入，值来自 .env）"""
    # 确保 system_configs 表存在（system.db）
    _db_gen = get_system_db() if _has_split_databases() else get_db()
    async for db in _db_gen:
        conn, cur = db
        await cur.execute("""
            CREATE TABLE IF NOT EXISTS system_configs (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                config_key VARCHAR(100) UNIQUE NOT NULL,
                config_value TEXT,
                config_type VARCHAR(20) DEFAULT 'string',
                category VARCHAR(50) DEFAULT 'general',
                description TEXT,
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
                updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
            )
        """)
        await conn.commit()
        break  # 只用一次连接建表

    defaults = {
        "chat_model": {
            "provider": "siliconflow",
            "api_base": settings.SILICONFLOW_API_BASE,
            "api_key": settings.SILICONFLOW_API_KEY or "",
            "model": getattr(settings, "SF_CHAT_MODEL", "Qwen/Qwen2.5-7B-Instruct"),
            "enabled": True,
        },
        "vision_model": {
            "provider": settings.VISION_PROVIDER,
            "api_base": settings.VISION_API_BASE,
            "api_key": settings.VISION_API_KEY,
            "model": settings.VISION_MODEL,
            "enabled": True,
        },
        "embedding_model": {
            "provider": "siliconflow",
            "api_base": settings.SILICONFLOW_API_BASE,
            "api_key": settings.SILICONFLOW_API_KEY or "",
            "model": "BAAI/bge-m3",
            "enabled": True,
        },
        "rerank_model": {
            "provider": "siliconflow",
            "api_base": settings.SILICONFLOW_API_BASE,
            "api_key": settings.SILICONFLOW_API_KEY or "",
            "model": "BAAI/bge-reranker-v2-m3",
            "enabled": True,
        },
    }

    async for db in _db_gen:
        conn, cur = db
        for scene_key, config in defaults.items():
            config_key = f"ai_scene_{scene_key}"
            await cur.execute(
                "SELECT id FROM system_configs WHERE config_key = %s", (config_key,)
            )
            row = await cur.fetchone()
            if not row:
                # 加密 API Key
                safe_config = {**config}
                if safe_config.get("api_key"):
                    safe_config["api_key"] = _encrypt_value(safe_config["api_key"])
                config_json = json.dumps(safe_config, ensure_ascii=False)
                await cur.execute(
                    """INSERT INTO system_configs (config_key, config_value, config_type, category, description)
                       VALUES (%s, %s, 'json', 'ai', %s)""",
                    (config_key, config_json, f"AI场景配置-{scene_key}"),
                )
                logger.info(f"初始化 AI 场景配置: {config_key}")
        await conn.commit()
