"""PFC Command Browse Tool - Navigate and retrieve command documentation."""

from typing import Any

from fastmcp import FastMCP
from pydantic import Field

from pfc_mcp.contracts import build_docs_data, build_error, build_ok
from pfc_mcp.knowledge.commands import CommandLoader
from pfc_mcp.utils import CommandDocVersion, normalize_command_doc_version, normalize_input


def register(mcp: FastMCP) -> None:
    """Register pfc_browse_commands tool with the MCP server."""

    @mcp.tool()
    def pfc_browse_commands(
        command: str | None = Field(
            None,
            description=(
                "PFC command to browse (space-separated, matching PFC syntax). Examples:\n"
                "- None or '': List all command categories\n"
                "- 'ball': List all ball commands\n"
                "- 'ball create': Get ball create documentation\n"
                "- 'contact': List all contact commands\n"
                "- 'contact property': Get contact property command documentation"
            ),
        ),
        version: CommandDocVersion = Field(
            CommandDocVersion.V7_0,
            description="PFC documentation version to browse. Defaults to 7.0.",
        ),
    ) -> dict[str, Any]:
        """Browse PFC command documentation by path (like glob + cat).

        Navigation levels:
        - No command: All command categories overview
        - Category only (e.g., "ball"): List commands in category
        - Full command (e.g., "ball create"): Full documentation

        When to use:
        - You know the command category or exact command
        - You want to explore available commands

        Related tools:
        - pfc_query_command: Search commands by keywords (when path unknown)
        - pfc_browse_reference: Browse reference docs (e.g., "contact-models linear")
        """
        cmd = normalize_input(command, lowercase=True)
        version_value = normalize_command_doc_version(version)

        if not cmd:
            return build_ok(_browse_root(version_value))

        parts = cmd.split()

        if len(parts) == 1:
            payload = _browse_category(parts[0], version_value)
        else:
            category = parts[0]
            command_name = " ".join(parts[1:])
            payload = _browse_command(category, command_name, version_value)
        return _wrap_payload(payload)


def _iter_available_category_commands(category: str, version: str) -> list[tuple[dict[str, Any], dict[str, Any]]]:
    """Return commands that are available in the requested version."""
    index = CommandLoader.load_index()
    category_data = index.get("categories", {}).get(category, {})
    available: list[tuple[dict[str, Any], dict[str, Any]]] = []

    for cmd_meta in category_data.get("commands", []):
        cmd_doc = CommandLoader.load_command_doc(category, cmd_meta.get("name", ""), version)
        if cmd_doc and cmd_doc.get("available") is not False:
            available.append((cmd_meta, cmd_doc))

    return available


def _browse_root(version: str) -> dict[str, Any]:
    """Level 0: Return overview of all command categories."""
    index = CommandLoader.load_index()
    categories = index.get("categories", {})
    category_items: list[dict[str, Any]] = []
    total_commands = 0

    for category_name, category_data in categories.items():
        available_commands = _iter_available_category_commands(category_name, version)
        command_count = len(available_commands)
        total_commands += command_count
        category_items.append(
            {
                "name": category_name,
                "description": category_data.get("description", ""),
                "command_count": command_count,
            }
        )

    return build_docs_data(
        source="commands",
        action="browse",
        entries=category_items,
        summary={
            "count": len(category_items),
            "total_commands": total_commands,
            "version": version,
        },
    )


def _browse_category(category: str, version: str) -> dict[str, Any]:
    """Level 1: Return list of commands in a category."""
    index = CommandLoader.load_index()
    categories = index.get("categories", {})

    if category not in categories:
        return {
            "source": "commands",
            "action": "browse",
            "error": {
                "code": "category_not_found",
                "message": f"Category '{category}' not found.",
            },
            "input": {"category": category, "version": version},
            "available_categories": sorted(categories.keys()),
        }

    cat_data = categories[category]
    command_items: list[dict[str, Any]] = []
    for cmd, cmd_doc in _iter_available_category_commands(category, version):
        command_items.append(
            {
                "name": cmd.get("name", ""),
                "short_description": cmd.get("short_description", ""),
                "syntax": cmd_doc.get("syntax"),
                "python_available": bool(cmd.get("python_available", False)),
            }
        )

    return build_docs_data(
        source="commands",
        action="browse",
        entries=command_items,
        summary={
            "count": len(command_items),
            "category": category,
            "description": cat_data.get("description", ""),
            "version": version,
        },
    )


def _browse_command(category: str, command_name: str, version: str) -> dict[str, Any]:
    """Level 2: Return full documentation for a specific command."""
    # JSON filenames use dash as sub-command separator (e.g. edge-create,
    # cmat-add, scalar-create) while PFC syntax separates them with spaces.
    # Accept either form on input.
    cmd_doc = CommandLoader.load_command_doc(category, command_name, version)
    if not cmd_doc and " " in command_name:
        cmd_doc = CommandLoader.load_command_doc(category, command_name.replace(" ", "-"), version)

    if not cmd_doc:
        index = CommandLoader.load_index()
        categories = index.get("categories", {})

        if category not in categories:
            return {
                "source": "commands",
                "action": "browse",
                "error": {
                    "code": "category_not_found",
                    "message": f"Category '{category}' not found.",
                },
                "input": {"category": category, "command": command_name, "version": version},
                "available_categories": sorted(categories.keys()),
            }

        available_cmds = [
            cmd_meta.get("name") for cmd_meta, _cmd_doc in _iter_available_category_commands(category, version)
        ]
        return {
            "source": "commands",
            "action": "browse",
            "error": {
                "code": "command_not_found",
                "message": f"Command '{command_name}' not found in '{category}'.",
            },
            "input": {"category": category, "command": command_name, "version": version},
            "available_commands": available_cmds,
        }

    if cmd_doc.get("available") is False:
        return {
            "source": "commands",
            "action": "browse",
            "error": {
                "code": "command_unavailable_for_version",
                "message": f"Command '{command_name}' is not available in PFC {version}.",
            },
            "input": {"category": category, "command": command_name, "version": version},
            "available_versions": cmd_doc.get("versions", []),
        }

    return build_docs_data(
        source="commands",
        action="browse",
        entries=[
            {
                "category": category,
                "command": command_name,
                "version": version,
                "doc": cmd_doc,
            }
        ],
        summary={"count": 1, "version": version},
    )


def _wrap_payload(payload: dict[str, Any]) -> dict[str, Any]:
    """Wrap tool payload into unified envelope."""
    if "error" in payload:
        err = payload.get("error") or {}
        details = {k: v for k, v in payload.items() if k != "error"}
        return build_error(
            code=str(err.get("code") or "browse_error"),
            message=str(err.get("message") or "Browse failed"),
            details=details or None,
        )
    return build_ok(payload)
