# Copyright (C) 2026 GoodData Corporation

"""Argument parser construction for the AIDA CLI."""

from __future__ import annotations

import argparse

from aida_mcp.server.cli_handlers_git_alias import (
    alias_install_cmd,
    alias_status_cmd,
    alias_uninstall_cmd,
    check_commit_policy_cmd,
    check_pr_policy_cmd,
    commit_cmd,
    preview_commit_template_cmd,
    preview_pr_template_cmd,
)
from aida_mcp.server.cli_handlers_lifecycle import (
    doctor_cmd,
    init_cmd,
    install_mcp,
    migrate_cmd,
    serve_http_cmd,
    update_rules_cmd,
)
from aida_mcp.server.cli_handlers_validation import validate_python_cmd


def _configure_validate_parser(validate_parser: argparse.ArgumentParser) -> None:
    validate_parser.add_argument("--workspace-root", help="Target workspace root (default: cwd).")
    validate_parser.add_argument(
        "--scope",
        choices=("pre_commit", "pre_push"),
        default="pre_commit",
        help="Validation scope (default: pre_commit).",
    )
    validate_parser.add_argument(
        "--focus-path",
        dest="focus_paths",
        action="append",
        help="Optional repo-relative focus path; repeat to pass multiple values.",
    )
    validate_parser.add_argument(
        "--upstream-ref",
        help="Optional upstream override for pre_push (for example @{u} or origin/main).",
    )
    validate_parser.add_argument(
        "--head-ref",
        default="HEAD",
        help="Head ref for pre_push comparison (default: HEAD).",
    )
    validate_parser.add_argument(
        "--dry-run",
        action="store_true",
        help="Plan and print steps without executing commands.",
    )
    validate_parser.add_argument(
        "--explain",
        action="store_true",
        help="Print resolved routing/command details for each step.",
    )
    validate_parser.add_argument(
        "--mode",
        choices=("important", "errors"),
        default="important",
        help="Which filtered lines to stream to stdout (default: important).",
    )
    validate_parser.add_argument(
        "--stdout-max-lines",
        type=int,
        default=120,
        help="Stop streaming filtered stdout after this many lines (default: 120).",
    )
    validate_parser.add_argument(
        "--stdout-max-chars",
        type=int,
        default=12000,
        help="Stop streaming filtered stdout after this many characters (default: 12000).",
    )
    validate_parser.add_argument(
        "--timeout-sec",
        type=int,
        help="Optional timeout override in seconds for every validation step.",
    )
    validate_parser.add_argument(
        "--pytest-output",
        choices=("plain", "default"),
        default="plain",
        help="How pytest should render nested test output. 'plain' is better for streaming clients.",
    )
    validate_parser.set_defaults(_handler=validate_python_cmd)


def build_arg_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(prog="aida-mcp", add_help=True)
    sub = parser.add_subparsers(dest="command")

    init = sub.add_parser(
        "init",
        help=(
            "Initialize this repo for AIDA (create .aida skeleton + repo-local MCP stubs for Cursor/Claude; "
            "JetBrains/Junie require --global install-mcp)."
        ),
    )
    init.add_argument("--workspace-root", help="Target workspace root (default: cwd).")
    init.add_argument("--force", action="store_true", help="Overwrite files where safe (stubs/config).")
    init.add_argument(
        "--http",
        action="store_true",
        help="Experimental: configure generated MCP client stubs to use the standard AIDA HTTP URL.",
    )
    init.add_argument(
        "--http-port",
        type=int,
        help="Experimental: port to write into generated HTTP MCP URLs (default: 9910).",
    )
    init.set_defaults(_handler=init_cmd)

    update_rules = sub.add_parser(
        "update-rules",
        help="Regenerate client rule/instruction files from the packaged onboarding template (always overwrites).",
    )
    update_rules.add_argument("--workspace-root", help="Target workspace root (default: cwd).")
    update_rules.set_defaults(_handler=update_rules_cmd)

    doctor = sub.add_parser("doctor", help="Check whether this repo is ready for AIDA.")
    doctor.add_argument("--workspace-root", help="Target workspace root (default: cwd).")
    doctor.add_argument(
        "--auto-fix",
        action="store_true",
        help="Apply safe semantic config fixes after reporting doctor findings.",
    )
    doctor.set_defaults(_handler=doctor_cmd)

    migrate = sub.add_parser("migrate", help="Migrate legacy repo/client setup to AIDA.")
    migrate.add_argument("--workspace-root", help="Target workspace root (default: cwd).")
    migrate.add_argument(
        "--client-only", action="store_true", help="Only migrate client instruction files (no config dir rename)."
    )
    migrate.add_argument("--dry-run", action="store_true", help="Report actions without writing/removing files.")
    migrate.add_argument("--force", action="store_true", help="Overwrite/replace when conflicts exist.")
    migrate.set_defaults(_handler=migrate_cmd)

    commit = sub.add_parser(
        "commit",
        help="Run pre-commit validation and create git commit with enforced footer policy.",
    )
    commit.add_argument("--workspace-root", help="Target workspace root (default: cwd).")
    commit.add_argument(
        "--plan",
        action="store_true",
        help="Resolve and print the policy-aware commit plan instead of creating a commit.",
    )
    commit.add_argument("--dry-run", action="store_true", help="Print composed message without creating a commit.")
    commit.add_argument("--json", action="store_true", help="Print machine-readable JSON output for `--plan`.")
    commit.add_argument("--type", default="chore", help="Commit type prefix (default: chore).")
    commit.add_argument("--scope", help="Optional commit scope, rendered as type(scope): ...")
    commit.add_argument("--title", help="Commit title text (subject line).")
    commit.add_argument("--fixup-of", help="Create a git autosquash fixup commit targeting this SHA/ref.")
    body_group = commit.add_mutually_exclusive_group()
    body_group.add_argument("--body", help="Commit body text (single argument; may include newlines).")
    body_group.add_argument("--body-file", help="Path to file containing commit body text.")
    body_group.add_argument("--body-stdin", action="store_true", help="Read commit body from stdin.")
    body_group.add_argument(
        "--body-line", action="append", help="Commit body line (repeatable). Lines are joined with newline."
    )
    commit.add_argument(
        "--risk",
        help="Risk footer value when git policy keeps risk trailers enabled (see .aida/git_policy.yaml).",
    )
    commit.add_argument(
        "--ticket",
        dest="ticket",
        action="append",
        help="Optional ticket ID (repeat flag for multiple values).",
    )
    commit.add_argument(
        "--co-authored-by", action="append", help="Optional co-author value (`Name <email>`). Repeatable."
    )
    commit.set_defaults(_handler=commit_cmd)

    check_commit_policy = sub.add_parser(
        "check-commit-policy",
        help="Check commit messages in a ref range against repository commit policy.",
    )
    check_commit_policy.add_argument("--workspace-root", help="Target workspace root (default: cwd).")
    check_commit_policy.add_argument(
        "--from-ref",
        required=True,
        help="Base ref (exclusive), for example @{u} or origin/main.",
    )
    check_commit_policy.add_argument(
        "--to-ref",
        default="HEAD",
        help="Head ref (inclusive), default: HEAD.",
    )
    check_commit_policy.add_argument(
        "--json",
        action="store_true",
        help="Print machine-readable JSON output.",
    )
    check_commit_policy.set_defaults(_handler=check_commit_policy_cmd)

    check_pr_policy = sub.add_parser(
        "check-pr-policy",
        help="Check PR title/body against repository PR template policy.",
    )
    check_pr_policy.add_argument("--workspace-root", help="Target workspace root (default: cwd).")
    check_pr_policy.add_argument("--title", required=True, help="PR title text.")
    check_pr_policy.add_argument("--body", default="", help="PR body text.")
    check_pr_policy.add_argument("--body-file", help="Path to a file containing PR body text.")
    check_pr_policy.add_argument("--json", action="store_true", help="Print machine-readable JSON output.")
    check_pr_policy.set_defaults(_handler=check_pr_policy_cmd)

    preview_commit = sub.add_parser(
        "preview-commit-template",
        help="Render commit template with placeholders into a concrete example.",
    )
    preview_commit.add_argument("--workspace-root", help="Target workspace root (default: cwd).")
    preview_commit.add_argument("--type", default="chore", help="Commit type placeholder.")
    preview_commit.add_argument("--scope", help="Optional commit scope placeholder.")
    preview_commit.add_argument("--title", required=True, help="Commit title placeholder.")
    preview_commit.add_argument("--risk", default="nonprod", help="Risk placeholder value.")
    preview_commit.add_argument(
        "--ticket",
        dest="ticket",
        action="append",
        help="Optional ticket placeholder value (repeatable).",
    )
    preview_commit.add_argument(
        "--co-authored-by", action="append", help="Optional Co-authored-by placeholder value (`Name <email>`)."
    )
    preview_commit.add_argument("--json", action="store_true", help="Print machine-readable JSON output.")
    preview_commit.set_defaults(_handler=preview_commit_template_cmd)

    preview_pr = sub.add_parser(
        "preview-pr-template",
        help="Render PR title/body templates with placeholders into concrete examples.",
    )
    preview_pr.add_argument("--workspace-root", help="Target workspace root (default: cwd).")
    preview_pr.add_argument("--title", required=True, help="PR title placeholder.")
    preview_pr.add_argument(
        "--ticket",
        dest="ticket",
        default="",
        help="Ticket placeholder value.",
    )
    preview_pr.add_argument("--risk", default="nonprod", help="Risk placeholder value.")
    preview_pr.add_argument("--summary", default="", help="Summary placeholder value.")
    preview_pr.add_argument("--test-plan", default="", help="Test plan placeholder value.")
    preview_pr.add_argument("--type", default="", help="Commit type placeholder value.")
    preview_pr.add_argument("--scope", default="", help="Scope placeholder value.")
    preview_pr.add_argument("--co-authored-by", default="", help="Co-authored-by placeholder value.")
    preview_pr.add_argument("--json", action="store_true", help="Print machine-readable JSON output.")
    preview_pr.set_defaults(_handler=preview_pr_template_cmd)

    install = sub.add_parser("install-mcp", help="Install MCP config for supported MCP clients")
    install.add_argument(
        "--target",
        action="append",
        choices=("cursor", "claude", "codex", "jetbrains", "junie", "all"),
        help="Target config(s) to install. Repeatable. Use 'all' for all targets.",
    )
    install.add_argument(
        "--global",
        dest="global_config",
        action="store_true",
        help=(
            "Write global ($HOME) client configs. Required for jetbrains/junie "
            "(~/.ai/mcp/mcp.json and ~/.junie/mcp/mcp.json)."
        ),
    )
    install.add_argument(
        "--workspace-root", help="Optional: pin `AIDA_WORKSPACE_ROOT` in the installed config (single-repo mode)."
    )
    install.add_argument(
        "--config-root", help="Optional: pin `AIDA_CONFIG_ROOT` in the installed config (single-repo mode)."
    )
    install.add_argument(
        "--http",
        action="store_true",
        help="Experimental: install MCP client config using the standard AIDA HTTP URL instead of stdio.",
    )
    install.add_argument(
        "--http-port",
        type=int,
        help="Experimental: port to write into generated HTTP MCP URLs (default: 9910).",
    )
    install.set_defaults(_handler=install_mcp)

    serve_http = sub.add_parser(
        "serve-http",
        help="Run the AIDA MCP server in foreground using Streamable HTTP transport.",
    )
    serve_http.add_argument("--host", default="127.0.0.1", help="Bind host (default: 127.0.0.1).")
    serve_http.add_argument(
        "--port",
        type=int,
        default=9910,
        help="Bind port (default: 9910).",
    )
    serve_http.add_argument("--path", default="/mcp", help="HTTP MCP path (default: /mcp).")
    serve_http.add_argument(
        "--workspace-root",
        help="Optional: pin the server to one workspace root via AIDA_WORKSPACE_ROOT.",
    )
    serve_http.set_defaults(_handler=serve_http_cmd)

    validate = sub.add_parser(
        "validate",
        help="Run repo-routed validation with streamed filtered output and capped spill logs.",
    )
    _configure_validate_parser(validate)

    validate_python = sub.add_parser(
        "validate-python",
        help="Alias for `validate` kept for backward compatibility.",
    )
    _configure_validate_parser(validate_python)

    alias = sub.add_parser("alias", help="Manage optional 'aida' CLI alias (wrapper next to aida-mcp).")
    alias_sub = alias.add_subparsers(dest="alias_command")

    alias_status = alias_sub.add_parser("status", help="Show alias status and PATH resolution")
    alias_status.add_argument("--name", default="aida", help="Alias command name (default: aida)")
    alias_status.set_defaults(_handler=alias_status_cmd)

    alias_install = alias_sub.add_parser("install", help="Install alias wrapper (fails if name exists on PATH)")
    alias_install.add_argument("--name", default="aida", help="Alias command name (default: aida)")
    alias_install.add_argument(
        "--force",
        action="store_true",
        help="Install even if the alias name already exists on PATH (PATH precedence still applies).",
    )
    alias_install.set_defaults(_handler=alias_install_cmd)

    alias_uninstall = alias_sub.add_parser("uninstall", help="Remove alias wrapper installed next to aida-mcp")
    alias_uninstall.add_argument("--name", default="aida", help="Alias command name (default: aida)")
    alias_uninstall.set_defaults(_handler=alias_uninstall_cmd)
    return parser
