"""提供商凭据与档位路由端点（C3 / ARCH §4.7, §7.2；UX §6.10）。

- GET  /settings/providers       列出已配置提供商（掩码）+ 档位路由。
- PUT  /settings/providers       幂等 upsert 凭据/档位路由，回掩码视图。
- POST /settings/providers/test  最小探测验 Key + 拉能力矩阵。

不变量：响应/日志绝不含明文 Key（加密入库、仅掩码出站）。
"""

from __future__ import annotations

from typing import Annotated

from fastapi import APIRouter, 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_api.logging_config import get_logger
from ww_api.schemas.providers import (
    CapabilitiesView,
    ProvidersResponse,
    ProvidersUpsertRequest,
    ProviderView,
    TestConnectionRequest,
    TestConnectionResponse,
    TierRoutingView,
)
from ww_api.security.credentials import encrypt_api_key, mask_api_key
from ww_api.services.credentials import (
    STUB_OWNER_ID,
    CredentialStore,
    ProviderProbe,
    StoredCredential,
    StoredRouting,
)
from ww_api.services.provider_deps import (
    get_credential_store,
    get_provider_probe,
)

log = get_logger("ww.api.providers")

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

StoreDep = Annotated[CredentialStore, Depends(get_credential_store)]
ProbeDep = Annotated[ProviderProbe, Depends(get_provider_probe)]
SessionDep = Annotated[AsyncSession, Depends(get_session)]


def _mask_credential(cred: StoredCredential, plaintext: str | None) -> ProviderView:
    # 存储层只有密文；掩码需明文末四位——若没有则全隐占位。
    masked = mask_api_key(plaintext) if plaintext is not None else mask_api_key("")
    return ProviderView(provider=cred.provider, masked_key=masked)


def _routing_view(r: StoredRouting) -> TierRoutingView:
    return TierRoutingView(tier=r.tier, provider=r.provider, model=r.model, fallback=r.fallback)


async def _build_response(store: CredentialStore) -> ProvidersResponse:
    creds = await store.list_credentials(STUB_OWNER_ID)
    routing = await store.list_routing()
    # 列表视图无明文，掩码占位（不解密历史密文以免无谓 IO/泄露面）。
    providers = [ProviderView(provider=c.provider, masked_key=mask_api_key("")) for c in creds]
    return ProvidersResponse(
        providers=providers,
        tier_routing=[_routing_view(r) for r in routing],
    )


@router.get("")
async def list_providers(store: StoreDep) -> ProvidersResponse:
    return await _build_response(store)


@router.put("")
async def upsert_providers(
    body: ProvidersUpsertRequest,
    store: StoreDep,
    session: SessionDep,
) -> ProvidersResponse:
    enc_key = get_settings().credential_enc_key.get_secret_value()
    for cred in body.credentials:
        api_key_enc = encrypt_api_key(cred.api_key, key=enc_key)
        await store.upsert_credential(STUB_OWNER_ID, cred.provider, api_key_enc)
        # 仅记 provider + 末四位掩码，绝不记明文
        log.info(
            "provider_credential_upserted",
            provider=cred.provider,
            masked_key=mask_api_key(cred.api_key),
        )
    for routing in body.tier_routing:
        await store.upsert_routing(
            StoredRouting(
                tier=routing.tier,
                provider=routing.provider,
                model=routing.model,
                fallback=routing.fallback,
            )
        )
        log.info(
            "tier_routing_upserted",
            tier=routing.tier,
            provider=routing.provider,
            model=routing.model,
        )
    # 所有凭据/路由写入只 flush；此处统一一次提交——任一步失败则整体回滚（原子性）。
    await session.commit()
    return await _build_response(store)


def _capabilities_view(caps: Capabilities) -> CapabilitiesView:
    return CapabilitiesView(
        structured_output=caps.structured_output,
        prefix_cache=caps.prefix_cache,
        thinking=caps.thinking,
    )


@router.post("/test")
async def test_connection(
    body: TestConnectionRequest,
    probe: ProbeDep,
) -> TestConnectionResponse:
    caps = await probe.probe(STUB_OWNER_ID, body.provider)
    log.info("provider_probe_ok", provider=body.provider)
    return TestConnectionResponse(
        provider=body.provider,
        ok=True,
        capabilities=_capabilities_view(caps),
    )
