# Copyright (C) 2026 GoodData Corporation

"""Pull request execution MCP tool."""

from __future__ import annotations

import re
from typing import TYPE_CHECKING, Annotated

from pydantic import Field

from aida_mcp.server.app_instance import Context
from aida_mcp.tools.core.git_policy_config import GitPolicy, load_git_policy
from aida_mcp.tools.core.template_loader import (
    build_pr_template_variables,
    render_pr_text,
    validate_pr_text_against_template,
)
from aida_mcp.utils.command_runner import CommandResult, run_command
from aida_mcp.utils.workspace import get_workspace_root

if TYPE_CHECKING:
    from mcp.server.fastmcp import FastMCP


def register(mcp: FastMCP) -> None:
    """Register pull request execution tools."""
    mcp.tool()(pr_command)


async def pr_command(
    base: Annotated[str, Field(description="Base branch for the pull request, for example master.")],
    head: Annotated[str, Field(description="Head branch for the pull request, for example feature/my-branch.")],
    title: Annotated[str, Field(description="PR title intent or subject text.")],
    summary: Annotated[str, Field(description="PR summary/body summary text.")] = "",
    test_plan: Annotated[str, Field(description="PR test plan text.")] = "",
    commit_type: Annotated[str, Field(description="Optional commit-style type prefix for title templates.")] = "",
    scope: Annotated[str | None, Field(description="Optional scope for title/body templates.")] = None,
    ticket: Annotated[str, Field(description="Optional ticket ID for PR templates.")] = "",
    risk: Annotated[str, Field(description="Policy-controlled risk value for PR templates.")] = "",
    co_authored_by: Annotated[
        str,
        Field(
            description="Policy-controlled Co-authored-by block for PR templates; may be required by .aida/git_policy.yaml."
        ),
    ] = "",
    draft: Annotated[bool, Field(description="When true, create the pull request as a draft.")] = False,
    *,
    ctx: Context,
) -> dict[str, object]:
    """Execute a policy-compliant gh pr create in the workspace."""
    resolved_root = await get_workspace_root(ctx)
    git_policy = load_git_policy(resolved_root)
    needs_more_input = _build_pr_needs_more_input(git_policy=git_policy, co_authored_by=co_authored_by)
    if needs_more_input is not None:
        return needs_more_input

    variables = build_pr_template_variables(
        title=title,
        ticket=ticket,
        risk=risk,
        summary=summary,
        test_plan=test_plan,
        commit_type=commit_type,
        scope=scope or "",
        co_authored_by=co_authored_by,
        ticket_enabled=git_policy.commit.ticket_enabled,
        ticket_prefix=git_policy.commit.ticket_prefix,
        risk_enabled=git_policy.commit.risk_enabled,
        risk_prefix=git_policy.commit.risk_prefix,
    )
    rendered = render_pr_text(workspace_root=resolved_root, policy=git_policy.pr, variables=variables)
    errors = validate_pr_text_against_template(
        workspace_root=resolved_root,
        policy=git_policy.pr,
        commit_policy=git_policy.commit,
        title=rendered["title"],
        body=rendered["body"],
    )
    if errors:
        return {
            "ok": False,
            "status": "error",
            "message": "; ".join(errors),
            "instruction": "The command failed. Do not retry without addressing the issue.",
        }

    cmd: list[str] = [
        "gh",
        "pr",
        "create",
        "--base",
        base,
        "--head",
        head,
        "--title",
        rendered["title"],
        "--body",
        rendered["body"],
    ]
    if draft:
        cmd.append("--draft")

    result: CommandResult = await run_command(
        cmd, cwd=resolved_root, timeout=60, ctx=ctx, step_name="Create pull request"
    )
    if result.exit_code != 0:
        return {
            "ok": False,
            "status": "error",
            "message": result.stderr.strip() or result.stdout.strip() or "gh pr create failed.",
            "instruction": "The command failed. Do not retry without addressing the issue.",
        }

    pr_url = result.stdout.strip()
    pr_number: int | None = None
    match = re.search(r"/pull/(\d+)", pr_url)
    if match:
        pr_number = int(match.group(1))

    return {
        "ok": True,
        "status": "created",
        "pr_url": pr_url,
        "pr_number": pr_number,
        "instruction": "Pull request created. Do not run any gh pr create commands.",
    }


def _build_pr_needs_more_input(*, git_policy: GitPolicy, co_authored_by: str) -> dict[str, object] | None:
    policy = git_policy.commit
    if not policy.require_co_authored_by or co_authored_by.strip():
        return None
    return {
        "ok": False,
        "status": "needs_more_input",
        "tool": "pr_command",
        "message": "Repository PR policy requires co_authored_by because commit policy requires it.",
        "missing_fields": ["co_authored_by"],
        "invalid_fields": [],
        "field_requirements": {
            "co_authored_by": {
                "required_by_policy": True,
                "format": "Name <email> or Co-authored-by: Name <email>",
                "trailer": "Co-authored-by",
                "inference_hint": "Use the active assistant identity for this conversation and retry.",
            }
        },
        "retry_instruction": (
            "Infer policy-controlled values from the current changes and retry pr_command. "
            "Do not ask the user for these fields unless inference is impossible."
        ),
    }
