from typing import Optional, List, Dict, Any
from .config import settings, _decrypt_value
from .database import get_db, get_system_db, _has_split_databases
import logging

logger = logging.getLogger(__name__)

_embedding_config_cache: Optional[Dict[str, str]] = None
_rerank_config_cache: Optional[Dict[str, str]] = None


def _decrypt_api_key(encrypted: str) -> str:
    """统一使用 config.py 的解密方法"""
    return _decrypt_value(encrypted)


async def _load_scene_config_from_system_configs(scene_key: str) -> Optional[Dict[str, str]]:
    """从 system_configs 读取场景配置（与 AI 配置页面统一）"""
    _db_gen = get_system_db() if _has_split_databases() else get_db()
    async for db in _db_gen:
        conn, cur = db
        try:
            config_map = {
                "embedding": "ai_scene_embedding_model",
                "rerank": "ai_scene_rerank_model",
            }
            db_key = config_map.get(scene_key)
            if not db_key:
                return None

            await cur.execute(
                "SELECT config_value FROM system_configs WHERE config_key = %s",
                (db_key,)
            )
            row = await cur.fetchone()
            if not row:
                return None

            val = row[0] if row else None
            if not val:
                return None

            import json
            data = json.loads(val) if isinstance(val, str) else val

            api_key = data.get("api_key", "")
            decrypted_key = _decrypt_api_key(api_key) if api_key else ""

            return {
                "model_name": data.get("model", ""),
                "api_base": data.get("api_base", ""),
                "api_key": decrypted_key,
            }
        except Exception as e:
            logger.error(f"从 system_configs 读取 {scene_key} 配置失败: {e}")
            return None
    return None


async def get_active_embedding_config() -> Dict[str, str]:
    global _embedding_config_cache
    if _embedding_config_cache is not None:
        return _embedding_config_cache
    # 优先从 system_configs（AI配置页面）读取
    cfg = await _load_scene_config_from_system_configs("embedding")
    if cfg and cfg.get("model_name") and cfg.get("api_base"):
        _embedding_config_cache = cfg
        return cfg
    # fallback 到 .env（云端 SiliconFlow，与 llm_client 一致）
    logger.warning("无法从 system_configs 读取 embedding 配置，回退到 .env 设置")
    _embedding_config_cache = {
        "model_name": "BAAI/bge-m3",
        "api_base": settings.SILICONFLOW_API_BASE,
        "api_key": getattr(settings, "SILICONFLOW_API_KEY", ""),
    }
    return _embedding_config_cache


async def get_active_rerank_config() -> Dict[str, str]:
    global _rerank_config_cache
    if _rerank_config_cache is not None:
        return _rerank_config_cache
    # 优先从 system_configs（AI配置页面）读取
    cfg = await _load_scene_config_from_system_configs("rerank")
    if cfg and cfg.get("model_name") and cfg.get("api_base"):
        _rerank_config_cache = cfg
        return cfg
    # fallback 到 .env（云端 SiliconFlow，与 llm_client 一致）
    logger.warning("无法从 system_configs 读取 rerank 配置，回退到 .env 设置")
    _rerank_config_cache = {
        "model_name": "BAAI/bge-reranker-v2-m3",
        "api_base": settings.SILICONFLOW_API_BASE,
        "api_key": getattr(settings, "SILICONFLOW_API_KEY", ""),
    }
    return _rerank_config_cache


def clear_model_config_cache():
    global _embedding_config_cache, _rerank_config_cache
    _embedding_config_cache = None
    _rerank_config_cache = None


async def _load_scene_config(scene_key: str) -> Optional[Dict[str, str]]:
    _db_gen = get_system_db() if _has_split_databases() else get_db()
    async for db in _db_gen:
        conn, cur = db
        try:
            await cur.execute(
                "SELECT config_value FROM system_configs WHERE config_key = 'model_selection_source'"
            )
            source_row = await cur.fetchone()
            config_value = source_row.get("config_value") if isinstance(source_row, dict) else (source_row[0] if source_row else "")
            if config_value != "bff_api":
                return None

            await cur.execute(
                "SELECT am.model_name, am.api_base, am.api_key "
                "FROM ai_scene_model_configs scm "
                "JOIN ai_models am ON scm.model_id = am.id "
                "WHERE scm.scene_key = %s AND scm.enabled = 1 AND am.enabled = 1 "
                "ORDER BY scm.priority ASC LIMIT 1",
                (scene_key,),
            )
            row = await cur.fetchone()
            if not row:
                return None

            model_name = row.get("model_name") if isinstance(row, dict) else row[0]
            api_base = row.get("api_base") if isinstance(row, dict) else row[1]
            api_key_encrypted = row.get("api_key") if isinstance(row, dict) else row[2]

            return {
                "model_name": model_name,
                "api_base": api_base,
                "api_key": _decrypt_api_key(api_key_encrypted),
            }
        except Exception as e:
            logger.error(f"读取 {scene_key} 场景模型配置失败: {e}", exc_info=True)
            return None
    return None


async def get_scene_model_config(
    scene_key: str, db, priority: int = None
) -> Optional[Dict[str, Any]]:
    conn, cur = db
    try:
        await cur.execute(
            "SELECT config_value FROM system_configs WHERE config_key = 'model_selection_source'"
        )
        source_row = await cur.fetchone()
        if not source_row:
            return None
        config_value = source_row.get("config_value") if isinstance(source_row, dict) else source_row[0]
        if config_value != "bff_api":
            return None

        await cur.execute(
            "SELECT scm.model_id, scm.priority, scm.config_json, "
            "am.model_name, am.api_base, am.api_key, am.provider_name, "
            "am.supports_thinking, am.supports_reasoning_effort, "
            "am.thinking_default_on "
            "FROM ai_scene_model_configs scm "
            "JOIN ai_models am ON scm.model_id = am.id "
            "WHERE scm.scene_key = %s AND scm.enabled = 1 AND am.enabled = 1 "
            "ORDER BY scm.priority ASC",
            (scene_key,),
        )
        rows = await cur.fetchall()
        if not rows:
            if scene_key != "user_chat":
                return await get_scene_model_config("user_chat", db, priority=priority)
            return None

        models = []
        for row in rows:
            config_json = _parse_json(row.get("config_json") if isinstance(row, dict) else row[2])
            model_config = {
                "model_id": row.get("model_id") if isinstance(row, dict) else row[0],
                "priority": row.get("priority") if isinstance(row, dict) else row[1],
                "config_override": config_json,
                "model_name": row.get("model_name") if isinstance(row, dict) else row[3],
                "api_base": row.get("api_base") if isinstance(row, dict) else row[4],
                "api_key": row.get("api_key") if isinstance(row, dict) else row[5],
                "provider": row.get("provider_name") if isinstance(row, dict) else row[6],
                "supports_thinking": bool(row.get("supports_thinking") if isinstance(row, dict) else row[7]),
                "supports_reasoning_effort": bool(row.get("supports_reasoning_effort") if isinstance(row, dict) else row[8]),
                "reasoning_params": config_json.get("reasoning_params", {}),
                "thinking_default_on": bool(row.get("thinking_default_on") if isinstance(row, dict) else row[9]),
            }
            models.append(model_config)

        if priority is not None:
            for m in models:
                if m["priority"] == priority:
                    return m
            return None

        return models[0] if models else None
    except Exception:
        return None


async def get_scene_model_fallback_chain(
    scene_key: str, db
) -> List[Dict[str, Any]]:
    conn, cur = db
    try:
        await cur.execute(
            "SELECT config_value FROM system_configs WHERE config_key = 'model_selection_source'"
        )
        source_row = await cur.fetchone()
        if not source_row:
            return []
        config_value = source_row.get("config_value") if isinstance(source_row, dict) else source_row[0]
        if config_value != "bff_api":
            return []

        await cur.execute(
            "SELECT scm.model_id, scm.priority, scm.config_json, "
            "am.model_name, am.api_base, am.api_key, am.provider_name "
            "FROM ai_scene_model_configs scm "
            "JOIN ai_models am ON scm.model_id = am.id "
            "WHERE scm.scene_key = %s AND scm.enabled = 1 AND am.enabled = 1 "
            "ORDER BY scm.priority ASC",
            (scene_key,),
        )
        rows = await cur.fetchall()

        models = []
        for row in rows:
            models.append({
                "model_id": row.get("model_id") if isinstance(row, dict) else row[0],
                "priority": row.get("priority") if isinstance(row, dict) else row[1],
                "config_override": _parse_json(row.get("config_json") if isinstance(row, dict) else row[2]),
                "model_name": row.get("model_name") if isinstance(row, dict) else row[3],
                "api_base": row.get("api_base") if isinstance(row, dict) else row[4],
                "api_key": row.get("api_key") if isinstance(row, dict) else row[5],
                "provider": row.get("provider_name") if isinstance(row, dict) else row[6],
            })
        return models

    except Exception:
        return []


def _parse_json(value) -> dict:
    if value is None:
        return {}
    if isinstance(value, dict):
        return value
    if isinstance(value, str):
        import json
        try:
            return json.loads(value)
        except (json.JSONDecodeError, TypeError):
            return {}
    return {}