"""LLM-based MCP tool routing.

The router receives only the user question and the discovered MCP tool
descriptors.  It never receives MCP bearer tokens or provider credentials.
"""

from __future__ import annotations

from dataclasses import dataclass
import json
from typing import Any, Mapping

from src.oci_genai_sdk import (
    build_oci_genai_completion_client,
    temperature_for_model_profile,
)
from src.poc3.model_registry import resolve_model_profile


@dataclass(frozen=True)
class McpTool:
    name: str
    description: str
    schema: Mapping[str, Any]
    read_only: bool


@dataclass(frozen=True)
class RoutedMcpTool:
    server_id: str
    tool: McpTool


class McpToolRouterError(RuntimeError):
    """Safe routing error. Must not contain secrets or provider traces."""


def route_mcp_tool_across_servers_with_llm(
    tools: list[RoutedMcpTool],
    question: str,
    *,
    router_model_profile: str,
) -> RoutedMcpTool:
    """Select one discovered MCP server/tool pair with OCI GenAI."""

    candidates = list(tools)
    if not candidates:
        raise McpToolRouterError("라우팅 가능한 MCP tool이 없습니다.")

    by_key = {
        "{}::{}".format(candidate.server_id, candidate.tool.name): candidate
        for candidate in candidates
    }
    route_keys = list(by_key)
    try:
        profile = resolve_model_profile(router_model_profile)
        client = build_oci_genai_completion_client(
            profile.model_id,
            profile.answer_model_region,
            profile.answer_model_endpoint,
        )
        tool_catalog = [
            {
                "route_key": "{}::{}".format(candidate.server_id, candidate.tool.name),
                "server_id": candidate.server_id,
                "tool_name": candidate.tool.name,
                "description": candidate.tool.description[:1000],
                "input_properties": sorted(
                    (
                        candidate.tool.schema.get("properties", {})
                        if isinstance(
                            candidate.tool.schema.get("properties"), Mapping
                        )
                        else {}
                    ).keys()
                ),
                "read_only": candidate.tool.read_only,
            }
            for candidate in candidates
        ]
        text = client.complete(
            system_prompt=(
                "You are an MCP server and tool router. Choose exactly one "
                "server/tool route for the user question from the discovered "
                "routes. Return only JSON that matches the schema. Never "
                "request or expose bearer tokens. Do not invent server ids or "
                "tool names."
            ),
            user_prompt=json.dumps(
                {
                    "question": question,
                    "routes": tool_catalog,
                },
                ensure_ascii=False,
            ),
            response_schema={
                "type": "object",
                "additionalProperties": False,
                "required": ["route_key"],
                "properties": {
                    "route_key": {
                        "type": "string",
                        "enum": route_keys,
                    }
                },
            },
            max_tokens=256,
            temperature=temperature_for_model_profile(profile),
        )
        routed = json.loads(text)
    except Exception:
        raise McpToolRouterError("LLM tool router 호출에 실패했습니다.") from None

    if not isinstance(routed, Mapping):
        raise McpToolRouterError("LLM tool router 응답 형식이 올바르지 않습니다.")
    selected_key = str(routed.get("route_key") or "").strip()
    selected = by_key.get(selected_key)
    if selected is None:
        raise McpToolRouterError("LLM tool router가 허용되지 않은 route를 선택했습니다.")
    return selected


def route_mcp_tool_with_llm(
    tools: list[McpTool],
    question: str,
    *,
    preferred_tool: str,
    tool_allowlist: tuple[str, ...],
    router_model_profile: str,
) -> McpTool:
    """Backward-compatible single-server routing helper."""

    candidates = [
        tool for tool in tools if not tool_allowlist or tool.name in tool_allowlist
    ]
    routed = route_mcp_tool_across_servers_with_llm(
        [RoutedMcpTool(server_id="default", tool=tool) for tool in candidates],
        question,
        router_model_profile=router_model_profile,
    )
    return routed.tool


def build_mcp_tool_arguments(
    tool: McpTool,
    question: str,
    limit: int,
    *,
    preferred_tool: str,
) -> dict[str, Any]:
    """Build bounded tool arguments from the selected tool schema."""

    properties = tool.schema.get("properties")
    if not isinstance(properties, Mapping):
        properties = {}

    if tool.name == preferred_tool:
        return {"prompt": question, "limit": limit}
    if "prompt" in properties:
        args: dict[str, Any] = {"prompt": question}
        if "limit" in properties:
            args["limit"] = limit
        elif "max_rows" in properties:
            args["max_rows"] = limit
        return args
    if "question" in properties:
        args = {"question": question}
        if "max_rows" in properties:
            args["max_rows"] = limit
        elif "limit" in properties:
            args["limit"] = limit
        return args
    if "query" in properties:
        args = {"query": question}
        if "max_evidence" in properties:
            args["max_evidence"] = min(limit, 10)
        elif "limit" in properties:
            args["limit"] = limit
        return args
    return {"prompt": question, "limit": limit}


__all__ = [
    "McpTool",
    "McpToolRouterError",
    "RoutedMcpTool",
    "build_mcp_tool_arguments",
    "route_mcp_tool_across_servers_with_llm",
    "route_mcp_tool_with_llm",
]
