"""提供商凭据端点的依赖装配（运行时实现）。

把 `get_session` 装配成 `SqlCredentialStore`；把网关适配器装配成探测器。
测试经 `app.dependency_overrides` 注入内存替身——不联网。
"""

from __future__ import annotations

import uuid
from typing import Annotated

from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession
from ww_config import get_settings
from ww_db import get_session
from ww_llm_gateway.adapters.base import Capabilities
from ww_llm_gateway.factory import build_adapter
from ww_shared import AppError, ErrorCode

from ww_api.security.credentials import (
    CredentialKeyError,
    decrypt_api_key,
)
from ww_api.services.credentials import (
    CredentialStore,
    SqlCredentialStore,
)

# 已知 OpenAI 兼容提供商 → base_url（ARCH §4.2）。
_PROVIDER_BASE_URLS: dict[str, str] = {
    "deepseek": "https://api.deepseek.com",
    "kimi": "https://api.moonshot.cn/v1",
    "qwen": "https://dashscope.aliyuncs.com/compatible-mode/v1",
    "glm": "https://open.bigmodel.cn/api/paas/v4",
    "openai": "https://api.openai.com/v1",
    # Kimi 订阅 plan（OAuth device-flow，K1.3）：coding 端点（OpenAI 兼容 + 伪造头）。
    "kimi-code": "https://api.kimi.com/coding/v1",
    # Kimi 订阅 plan（静态 Console Key，ToS 合规）：同一 coding 端点，纯 bearer 无伪造头。
    "kimi-code-key": "https://api.kimi.com/coding/v1",
}


def get_credential_store(
    session: Annotated[AsyncSession, Depends(get_session)],
) -> CredentialStore:
    return SqlCredentialStore(session)


class GatewayProviderProbe:
    """运行时探测：解密 Key→建 OpenAI 兼容适配器→最小请求验 Key→回能力矩阵。

    依赖 store 取密文 + settings 取加密 key。绝不在日志/响应回显明文。
    """

    def __init__(self, store: CredentialStore, enc_key: str) -> None:
        self._store = store
        self._enc_key = enc_key

    async def probe(self, owner_id: uuid.UUID, provider: str) -> Capabilities:
        cred = await self._store.get_credential(owner_id, provider)
        if cred is None:
            raise AppError(
                ErrorCode.NOT_FOUND,
                f"provider {provider} 未配置凭据",
                {"provider": provider},
            )
        base_url = _PROVIDER_BASE_URLS.get(provider)
        if base_url is None:
            raise AppError(
                ErrorCode.VALIDATION,
                f"未知提供商 {provider}",
                {"provider": provider},
            )
        if cred.api_key_enc is None:
            # api_key 探测不支持 OAuth 凭据（无 api_key 密文）——OAuth provider 走专属端点。
            raise AppError(
                ErrorCode.VALIDATION,
                f"provider {provider} 为 OAuth 凭据，不支持 api_key 连接测试",
                {"provider": provider},
            )
        try:
            api_key = decrypt_api_key(cred.api_key_enc, key=self._enc_key)
        except CredentialKeyError as exc:
            raise AppError(ErrorCode.INTERNAL, str(exc)) from exc

        # 经网关工厂构造适配器（base_url 单点归网关 build_adapter，不在此本地构造 AsyncOpenAI）。
        adapter = build_adapter(provider, api_key=api_key, base_url=base_url)
        try:
            # 最小探测：列模型即可验证 Key 有效（不消耗生成额度）。底层 AsyncOpenAI 由适配器持有
            # （已知 provider 为 OpenAI 兼容，见 _PROVIDER_BASE_URLS）；读私有客户端属同仓既有约定。
            await adapter._client.models.list()  # type: ignore[attr-defined]  # noqa: SLF001
        except Exception as exc:  # noqa: BLE001 — 任一失败都映射为 LLM 不可用
            raise AppError(
                ErrorCode.LLM_UNAVAILABLE,
                f"provider {provider} 连接探测失败",
                {"provider": provider},
            ) from exc
        return adapter.capabilities()


def get_provider_probe(
    store: Annotated[CredentialStore, Depends(get_credential_store)],
) -> GatewayProviderProbe:
    return GatewayProviderProbe(store, get_settings().credential_enc_key.get_secret_value())
