"""Tool list construction and name resolution shared across the core.

`build_tool_list` is what the Agent and the SubAgentRunner use to turn a
profile's config into concrete tools; `resolve_tool` is how a model-written
tool name finds one of them. Both live here so every consumer — the executor
and tool_manual — answers "which tool did the model mean" the same way.
"""

import logging
from pathlib import Path

from navi.config import settings
from navi.tools._internal.base import Tool

log = logging.getLogger(__name__)

_USER_ENABLED_FILE = Path(settings.tools_dir) / "enabled.json"


def load_user_enabled_tools() -> list[str]:
    try:
        import json
        return json.loads(_USER_ENABLED_FILE.read_text())
    except Exception:
        return []


def resolve_tool(tool_map: dict[str, Tool], name: str) -> tuple[str, Tool | None]:
    """Resolve a requested name against a tool map, tolerating the ways models
    mangle MCP names.

    Returns the key to report ('requested name' when nothing matched) and the
    tool. The variants below are all observed in stored sessions: a bare tool
    name, a dash/underscore swap, the old `mcp_<server>_<tool>` spelling, and
    the legacy `mcp:server:tool` one. Every consumer must share this, or the
    same call resolves in the executor and 404s in the tool that documents it.
    """
    from navi.mcp.tools import is_mcp_tool, parse_mcp_name

    tool = tool_map.get(name)
    if tool is not None:
        return name, tool

    # Support bare tool name when the full MCP name ends with it
    # e.g. "web_search" -> "mcp__navi-web__web_search"
    bare_matches = [
        (candidate_name, candidate)
        for candidate_name, candidate in tool_map.items()
        if is_mcp_tool(candidate_name) and candidate_name.endswith(f"__{name}")
    ]
    if len(bare_matches) == 1:
        return bare_matches[0]

    # Normalized variant (dash vs underscore)
    normalized = name.replace("-", "_")
    normalized_matches = [
        (candidate_name, candidate)
        for candidate_name, candidate in tool_map.items()
        if is_mcp_tool(candidate_name) and candidate_name.replace("-", "_") == normalized
    ]
    if len(normalized_matches) == 1:
        return normalized_matches[0]

    # Fallback: old underscore format like mcp_server_tool -> mcp__server__tool
    old_format_matches = [
        (candidate_name, candidate)
        for candidate_name, candidate in tool_map.items()
        if is_mcp_tool(candidate_name) and name.startswith("mcp_")
    ]
    for candidate_name, candidate in old_format_matches:
        parsed = parse_mcp_name(candidate_name)
        if parsed is None:
            continue
        server_name, tool_name = parsed
        # mcp_navi_web_search -> navi_web_search -> split into navi, web, search
        # We try matching by removing the mcp_ prefix and comparing
        expected_old = f"mcp_{server_name}_{tool_name}"
        if expected_old == name:
            return candidate_name, candidate

    # Extra fallback: legacy colon format mcp:server:tool (from old sessions)
    legacy_matches = [
        (candidate_name, candidate)
        for candidate_name, candidate in tool_map.items()
        if is_mcp_tool(candidate_name) and name.replace(":", "__") == candidate_name
    ]
    if len(legacy_matches) == 1:
        return legacy_matches[0]

    return name, None


def build_tool_list(
    enabled: list[str],
    mcp_servers: dict[str, list[str]] | None,
    tool_registry,
    mcp_manager,
) -> list[Tool]:
    """Resolve enabled tool names + MCP groups into concrete Tool objects."""
    names = list(enabled)
    extra = load_user_enabled_tools()
    for name in extra:
        if name not in names:
            names.append(name)

    # Expand MCP server groups into concrete tool names
    from navi.mcp.tools import build_mcp_name

    if mcp_servers and mcp_manager:
        for server_name, groups in mcp_servers.items():
            if "*" in groups:
                prefix = build_mcp_name(server_name, "")
                for tool in tool_registry.all():
                    if tool.name.startswith(prefix) and tool.name not in names:
                        names.append(tool.name)
            else:
                for group_name in groups:
                    for tool_name in mcp_manager.resolve_group(server_name, group_name):
                        full_name = build_mcp_name(server_name, tool_name)
                        if full_name not in names:
                            names.append(full_name)

    result = []
    missing = []
    for name in names:
        try:
            result.append(tool_registry.get(name))
        except Exception:
            missing.append(name)
    if missing:
        log.warning("build_tool_list missing tools: %s", missing)
    return result
