"""MCP 发布路由 — 将 Skill / 专家团队发布为 MCP 工具

U14: 管理员通过这些端点把已注册的 Skill 或专家团队封装成 MCP 工具，
注册到 ``app.state.mcp_publisher_registry`` 后即可通过 MCP 端点对外暴露。

所有发布/取消发布/列表端点均要求 ``SYSTEM_CONFIG`` 权限（admin）。
已发布的工具本身通过 ``/api/v1/mcp/tools/list`` 等 member 级端点调用。
"""

from __future__ import annotations

import logging
from typing import Any

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

from agentkit.mcp.publisher import (
    PublisherRegistry,
    SkillMCPAdapter,
    TeamMCPAdapter,
    check_dangerous_publish,
)
from agentkit.server.auth.dependencies import require_permission
from agentkit.server.auth.permissions import Permission

logger = logging.getLogger(__name__)

router = APIRouter(tags=["mcp-publish"])

# 发布端点要求 admin（SYSTEM_CONFIG）权限——防止普通用户把高危能力暴露给外部系统。
_admin_auth = require_permission(Permission.SYSTEM_CONFIG)


class SkillPublishRequest(BaseModel):
    """Skill 发布请求体。"""

    model_config = ConfigDict(extra="forbid")

    allow_dangerous: bool = False
    description_override: str | None = None


class TeamPublishRequest(BaseModel):
    """专家团队发布请求体。"""

    model_config = ConfigDict(extra="forbid")

    description: str | None = None
    allow_dangerous: bool = False


def _get_publisher_registry(request: Request) -> PublisherRegistry:
    """获取 app.state 上的 PublisherRegistry；不存在则 500。"""
    registry = getattr(request.app.state, "mcp_publisher_registry", None)
    if registry is None:
        raise HTTPException(
            status_code=500,
            detail="mcp_publisher_registry not configured on app.state",
        )
    return registry


def _build_skill_executor(app_state: Any):
    """构造 Skill 执行器闭包。

    lazy MVP: 仅当 ``app.state.agent_pool`` 存在且实现了 ``run_skill`` 时调用；
    否则返回错误 dict（不阻塞发布，调用时才报错）。
    ponytail: 完整的 agent 执行接线推迟到后续迭代。
    """

    async def _executor(skill_name: str, input_text: str) -> dict[str, Any]:
        pool = getattr(app_state, "agent_pool", None)
        if pool is None:
            return {"error": "agent_pool not available"}
        run_skill = getattr(pool, "run_skill", None)
        if run_skill is None:
            return {"error": "agent_pool.run_skill not implemented"}
        return await run_skill(skill_name, input_text)

    return _executor


def _build_team_executor(app_state: Any):
    """构造专家团队执行器闭包。"""

    async def _executor(team_name: str, input_text: str) -> dict[str, Any]:
        pool = getattr(app_state, "agent_pool", None)
        if pool is None:
            return {"error": "agent_pool not available"}
        run_team = getattr(pool, "run_team", None)
        if run_team is None:
            return {"error": "agent_pool.run_team not implemented"}
        return await run_team(team_name, input_text)

    return _executor


@router.post("/mcp/publish/skill/{skill_name}")
async def publish_skill(
    skill_name: str,
    body: SkillPublishRequest,
    request: Request,
    _user: dict = Depends(_admin_auth),
) -> dict[str, Any]:
    """发布一个 Skill 为 MCP 工具。

    - Skill 不存在 → 404
    - Skill 含危险工具且未显式 allow_dangerous → 403
    - 同名工具已发布 → 409
    """
    skill_registry = getattr(request.app.state, "skill_registry", None)
    if skill_registry is None:
        raise HTTPException(status_code=500, detail="skill_registry not configured")

    try:
        skill = skill_registry.get(skill_name)
    except Exception:
        raise HTTPException(status_code=404, detail=f"Skill '{skill_name}' not found")

    try:
        check_dangerous_publish(skill, body.allow_dangerous)
    except ValueError as e:
        raise HTTPException(status_code=403, detail=str(e))

    executor = _build_skill_executor(request.app.state)
    description = body.description_override or None
    adapter = SkillMCPAdapter(skill, executor=executor)
    if description is not None:
        adapter.description = description

    registry = _get_publisher_registry(request)
    try:
        registry.register(adapter)
    except ValueError as e:
        raise HTTPException(status_code=409, detail=str(e))

    return {"published": adapter.name, "type": "skill"}


@router.post("/mcp/publish/team/{team_name}")
async def publish_team(
    team_name: str,
    body: TeamPublishRequest,
    request: Request,
    _user: dict = Depends(_admin_auth),
) -> dict[str, Any]:
    """发布一个专家团队为 MCP 工具。

    - 同名工具已发布 → 409
    """
    executor = _build_team_executor(request.app.state)
    adapter = TeamMCPAdapter(
        team_name,
        executor=executor,
        description=body.description,
    )

    registry = _get_publisher_registry(request)
    try:
        registry.register(adapter)
    except ValueError as e:
        raise HTTPException(status_code=409, detail=str(e))

    return {"published": adapter.name, "type": "team"}


@router.delete("/mcp/publish/{name}")
async def unpublish(
    name: str,
    request: Request,
    _user: dict = Depends(_admin_auth),
) -> dict[str, Any]:
    """取消发布一个 MCP 工具。不存在 → 404。"""
    registry = _get_publisher_registry(request)
    if not registry.unregister(name):
        raise HTTPException(status_code=404, detail=f"Published tool '{name}' not found")
    return {"unpublished": name}


@router.get("/mcp/publish")
async def list_published(
    request: Request,
    _user: dict = Depends(_admin_auth),
) -> dict[str, Any]:
    """列出所有已发布的 MCP 工具。"""
    registry = _get_publisher_registry(request)
    tools = registry.list_published()
    return {
        "published": [
            {
                "name": t.name,
                "type": t.tags[0] if t.tags else "unknown",
                "description": t.description,
            }
            for t in tools
        ]
    }
