"""
api.v1.mcp — MCP server 注册中心 API（docs/MCP_SKILL_DESIGN.md P1）。

GET    /api/v1/mcp                列表（脱敏：auth 只出现 env_key 名）
POST   /api/v1/mcp                添加（loopback-only + stdio argv allowlist）
PUT    /api/v1/mcp/{id}           修改 / 启停
DELETE /api/v1/mcp/{id}           移除
POST   /api/v1/mcp/{id}/probe    连接测试 + 刷新工具清单

添加/修改 server 是本机配置操作（指向本机命令或外部端点），与 /setup、
agentpack install 同样 loopback-only（远程调用者不该改本机连接配置）。
"""
from __future__ import annotations

from typing import List, Optional

from fastapi import APIRouter, Depends, HTTPException, Request
from pydantic import BaseModel, Field

from agentpaas.api.deps import get_tenant
from agentpaas.api.middleware.auth import TenantContext
from agentpaas.engine import mcp_registry as reg

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


def _loopback_only(request: Request) -> None:
    host = (request.client.host if request.client else "") or ""
    if host not in ("127.0.0.1", "::1", "localhost", "testclient"):
        raise HTTPException(status_code=404, detail="Not Found")


class AuthSpec(BaseModel):
    kind: str = "none"            # none | env_bearer | env_header
    env_key: str = ""
    header_name: str = "Authorization"


class ServerSpec(BaseModel):
    id: str = Field(..., min_length=1)
    name: str = ""
    transport: str = "stdio"      # stdio | sse | http
    command_argv: List[str] = Field(default_factory=list)
    url: str = ""
    auth: AuthSpec = Field(default_factory=AuthSpec)
    enabled: bool = True
    tool_risk_overrides: dict = Field(default_factory=dict)
    # 信任时显式给 stdio 可执行加白（评审#16）
    allow_argv: List[str] = Field(default_factory=list)


class UpdateSpec(BaseModel):
    name: Optional[str] = None
    transport: Optional[str] = None
    command_argv: Optional[List[str]] = None
    url: Optional[str] = None
    auth: Optional[AuthSpec] = None
    enabled: Optional[bool] = None
    tool_risk_overrides: Optional[dict] = None
    allow_argv: List[str] = Field(default_factory=list)


@router.get("")
async def list_mcp(_tenant: TenantContext = Depends(get_tenant)):
    return {"servers": reg.list_servers()}


@router.post("", status_code=201)
async def add_mcp(spec: ServerSpec, request: Request,
                  _tenant: TenantContext = Depends(get_tenant)):
    _loopback_only(request)
    allowlist = reg.DEFAULT_ARGV_ALLOWLIST | set(spec.allow_argv or [])
    payload = spec.model_dump(exclude={"allow_argv"})
    try:
        view = reg.upsert_server(payload, argv_allowlist=allowlist)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    return {"ok": True, "server": view}


@router.put("/{server_id}")
async def update_mcp(server_id: str, body: UpdateSpec, request: Request,
                     _tenant: TenantContext = Depends(get_tenant)):
    _loopback_only(request)
    cur = reg.get_server(server_id)
    if not cur:
        raise HTTPException(status_code=404, detail="MCP server not found")
    merged = {**cur, "id": server_id}
    for k, v in body.model_dump(exclude={"allow_argv"}).items():
        if v is not None:
            merged[k] = v
    allowlist = reg.DEFAULT_ARGV_ALLOWLIST | set(body.allow_argv or [])
    try:
        view = reg.upsert_server(merged, argv_allowlist=allowlist)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    return {"ok": True, "server": view}


@router.delete("/{server_id}")
async def delete_mcp(server_id: str, request: Request,
                     _tenant: TenantContext = Depends(get_tenant)):
    _loopback_only(request)
    if not reg.remove_server(server_id):
        raise HTTPException(status_code=404, detail="MCP server not found")
    return {"ok": True}


@router.post("/{server_id}/probe")
async def probe_mcp(server_id: str, _tenant: TenantContext = Depends(get_tenant)):
    if not reg.get_server(server_id):
        raise HTTPException(status_code=404, detail="MCP server not found")
    return reg.probe_server(server_id)
