# Copyright (C) 2026 GoodData Corporation

"""MCP validation planning tool registration."""

from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING, Annotated, Literal

from pydantic import Field

from aida_mcp.output import shrink_tool_payload
from aida_mcp.server.app_instance import Context
from aida_mcp.server.commit_policy_checks import check_commit_policy_range
from aida_mcp.server.validation_scope import resolve_validation_scope
from aida_mcp.tools.core.git_policy_config import load_git_policy
from aida_mcp.tools.validate.engine import NO_MATCHING_VALIDATION_ROUTES_TEXT, build_resolved_plan_payload
from aida_mcp.utils.repo_readiness import check_repo_readiness
from aida_mcp.utils.workspace import get_workspace_root

if TYPE_CHECKING:
    from mcp.server.fastmcp import FastMCP


def _run_pre_push_commit_policy_checks(
    *,
    workspace_root: str,
    resolved_upstream: str,
    head_ref: str,
) -> dict[str, object]:
    result = check_commit_policy_range(
        workspace_root=Path(workspace_root),
        from_ref=resolved_upstream,
        to_ref=head_ref,
    )
    result["text"] = "Commit policy checks passed." if result["success"] else "Commit policy checks failed."
    result["checker_command"] = f"aida-mcp check-commit-policy --from-ref {resolved_upstream} --to-ref {head_ref}"
    return result


def _build_pre_commit_guidance(*, workspace_root: str) -> dict[str, object]:
    policy = load_git_policy(Path(workspace_root)).commit
    return {
        "tool": "commit_command",
        "risk_values": list(policy.risk_values),
        "text": (
            "Before committing, call the MCP `commit_command` tool. "
            "It executes the commit server-side. If it returns `ok=true`, the commit is done. "
            "If it returns `status=needs_more_input`, infer the missing policy-controlled values from the current changes and retry."
        ),
    }


def register(mcp: FastMCP) -> None:
    """Register validation planning tool."""
    mcp.tool()(validate_command)


async def validate_command(
    scope: Annotated[
        Literal["pre_commit", "pre_push"],
        Field(
            description=(
                "Validation scope:\n"
                "- pre_commit: current working tree changes\n"
                "- pre_push: commits vs upstream + dirty working tree"
            )
        ),
    ],
    focus_paths: Annotated[
        list[str] | None,
        Field(
            description=("Optional repo-relative files/dirs to validate directly. Skips git-based change discovery.")
        ),
    ] = None,
    explain: Annotated[
        bool,
        Field(description=("If true, include routing details and planned commands.")),
    ] = False,
    upstream_ref: Annotated[
        str | None,
        Field(description=("Optional upstream override for pre_push (for example '@{u}' or 'origin/main').")),
    ] = None,
    head_ref: Annotated[
        str,
        Field(description="Head ref override (default: HEAD). Primarily for tests/CI."),
    ] = "HEAD",
    *,
    ctx: Context,
) -> object:
    """Plan repository-defined validation commands for pre_commit or pre_push scope."""
    workspace_root = await get_workspace_root(ctx)

    readiness = check_repo_readiness(workspace_root=workspace_root)
    if not readiness.validate_ready:
        missing = []
        if not readiness.config_root_exists:
            missing.append(f"missing config dir: {readiness.config_root}")
        else:
            missing.extend(
                [f"missing file: {readiness.config_root}/{name}" for name in readiness.missing_validate_files]
            )
        missing_text = "\n".join(f"- {m}" for m in missing) if missing else "- (unknown)"
        return shrink_tool_payload(
            {
                "tool": "validate_command",
                "success": False,
                "text": (
                    "Repository is not initialized for AIDA validation.\n\n"
                    f"{missing_text}\n\n"
                    "Initialize this repo (recommended): `aida-mcp init`."
                ),
                "scope": scope,
                "workspace_root": str(workspace_root),
            }
        )

    try:
        selection = resolve_validation_scope(
            workspace_root=workspace_root,
            scope=scope,
            focus_paths=focus_paths,
            upstream_ref=upstream_ref,
            head_ref=head_ref,
        )
    except ValueError as exc:
        return {
            "tool": "validate_command",
            "success": False,
            "text": str(exc),
            "scope": scope,
            "workspace_root": str(workspace_root),
        }

    payload = build_resolved_plan_payload(
        workspace_root=workspace_root,
        scope=scope,
        changed_paths=selection.changed_paths,
        include_explain=explain,
    )
    # Preserve scope metadata.
    payload.setdefault("scope_source", selection.scope_source)
    payload.setdefault("workspace_root", str(workspace_root))
    payload.setdefault("focus_paths", focus_paths or [])
    payload.setdefault("explain", explain)
    if scope == "pre_commit":
        pre_commit_guidance_enabled = load_git_policy(Path(workspace_root)).commit.pre_commit_guidance
        if pre_commit_guidance_enabled:
            payload["commit_guidance"] = _build_pre_commit_guidance(workspace_root=str(workspace_root))
    if scope == "pre_push":
        payload.setdefault("resolved_upstream_ref", selection.resolved_upstream_ref)
        payload.setdefault("upstream_source", selection.upstream_source)
        payload.setdefault("upstream_ref", selection.upstream_ref)
        payload.setdefault("head_ref", selection.head_ref)
        if selection.resolved_upstream_ref:
            commit_policy = _run_pre_push_commit_policy_checks(
                workspace_root=str(workspace_root),
                resolved_upstream=selection.resolved_upstream_ref,
                head_ref=selection.head_ref,
            )
            payload["commit_policy"] = commit_policy
            if not commit_policy.get("success", True):
                payload["success"] = False
                existing_text = str(payload.get("text", "")).strip()
                payload["text"] = (
                    f"{existing_text}\n❌ pre_push commit policy checks failed"
                    if existing_text
                    else "❌ pre_push commit policy checks failed"
                )
    # For focus_paths we expect deterministic routing. If nothing matches, return an explicit error.
    if selection.scope_source == "focus_paths" and payload.get("text") == NO_MATCHING_VALIDATION_ROUTES_TEXT:
        payload["success"] = False
        payload["text"] = (
            "No matching validation routes for focus_paths. "
            "Pass a file path or any directory under a configured component root."
        )
    return shrink_tool_payload(payload)
