from pathlib import Path

"""
Base parser module for claude-mpm CLI.

WHY: This module contains the main parser factory and common argument definitions
that are shared across all commands. Extracted from the monolithic parser.py.

DESIGN DECISION: Common arguments are defined once and reused to ensure consistency
and reduce duplication across command parsers.
"""

import argparse
import sys

from ...constants import CLICommands, CLIPrefix, LogLevel


class SuggestingArgumentParser(argparse.ArgumentParser):
    """
    Custom ArgumentParser that suggests similar commands on error.

    WHY: Provides better user experience by suggesting corrections for typos
    and invalid commands instead of just showing an error message.

    DESIGN DECISION: Extends ArgumentParser.error() to add suggestions before
    exiting. This catches all parser errors including invalid subcommands and
    invalid options.
    """

    def error(self, message: str) -> None:
        """
        Override error method to add command suggestions.

        Args:
            message: Error message from argparse
        """
        from ..utils import suggest_similar_commands

        # Try to extract the invalid command/option from the error message
        invalid_value = None
        valid_choices = []

        # Handle invalid subcommand errors
        # Format: "argument COMMAND: invalid choice: 'tickts' (choose from ...)"
        if "invalid choice:" in message:
            try:
                # Extract the invalid choice
                parts = message.split("invalid choice: '")
                if len(parts) > 1:
                    invalid_value = parts[1].split("'")[0]

                # Extract valid choices
                if "(choose from" in message:
                    choices_part = message.split("(choose from")[1]
                    # Remove trailing parenthesis and split
                    choices_str = choices_part.rstrip(")")
                    # Parse choices - they may be quoted or unquoted
                    valid_choices = [
                        c.strip().strip("'\"")
                        for c in choices_str.split(",")
                        if c.strip()
                    ]
            except (IndexError, ValueError):
                pass

        # Handle unrecognized arguments (invalid options)
        # Format: "unrecognized arguments: --verbos"
        elif "unrecognized arguments:" in message:
            try:
                parts = message.split("unrecognized arguments:")
                if len(parts) > 1:
                    invalid_value = parts[1].strip().split()[0]

                # Get common options from parser
                valid_choices = []
                for action in self._actions:
                    for option in action.option_strings:
                        valid_choices.append(option)
            except (IndexError, ValueError):
                pass

        # Build error message with suggestions
        from rich.console import Console

        console = Console(stderr=True)

        console.print(f"\n[red]Error:[/red] {message}\n", style="bold")

        # Add suggestions if we found valid choices
        if invalid_value and valid_choices:
            suggestion = suggest_similar_commands(invalid_value, valid_choices)
            if suggestion:
                console.print(f"[yellow]{suggestion}[/yellow]\n")

        # Show help hint
        console.print(f"[dim]Run '{self.prog} --help' for usage information.[/dim]\n")

        # Exit with error code
        sys.exit(2)


def _get_enhanced_version(base_version: str) -> str:
    """
    Get enhanced version string with build number if available.

    Args:
        base_version: Base version string (e.g., "4.0.8")

    Returns:
        Enhanced version string with build number if available
    """
    try:
        # Try to use VersionService for enhanced version display
        from ...services.version_service import VersionService

        version_service = VersionService()
        enhanced = version_service.get_version()

        # If we got an enhanced version (with build number), use it
        # Remove the 'v' prefix since argparse will add the program name
        if enhanced and enhanced.startswith("v"):
            enhanced = enhanced[1:]  # Remove 'v' prefix

        if enhanced and enhanced != base_version:
            return enhanced
    except Exception:  # nosec B110
        # If anything fails, fall back to base version
        pass

    return base_version


def add_common_arguments(
    parser: argparse.ArgumentParser, version: str | None = None
) -> None:
    """
    Add common arguments that apply to all commands.

    WHY: These arguments are needed across multiple commands, so we centralize them
    to ensure consistency and avoid duplication.

    Args:
        parser: The argument parser to add arguments to
        version: Version string to display (only needed for main parser)
    """
    # Version - only add to main parser, not subparsers
    if version is not None:
        # Use enhanced version display with build number if available
        enhanced_version = _get_enhanced_version(version)
        parser.add_argument(
            "--version", action="version", version=f"%(prog)s {enhanced_version}"
        )

    # Logging arguments
    logging_group = parser.add_argument_group("logging options")
    logging_group.add_argument(
        "-d",
        "--debug",
        action="store_true",
        help="Enable debug logging (deprecated, use --logging DEBUG)",
    )
    logging_group.add_argument(
        "-v",
        "--verbose",
        action="store_true",
        help="Enable verbose logging (deprecated, use --logging INFO)",
    )
    logging_group.add_argument(
        "-q",
        "--quiet",
        action="store_true",
        help="Suppress all output except errors (deprecated, use --logging ERROR)",
    )
    logging_group.add_argument(
        "--logging",
        choices=[level.value for level in LogLevel],
        help="Set logging level (overrides -d, -v, -q flags)",
    )

    # Configuration arguments
    config_group = parser.add_argument_group("configuration options")
    config_group.add_argument("--config", type=Path, help="Path to configuration file")
    config_group.add_argument(
        "--project-dir", type=Path, help="Project directory (overrides auto-detection)"
    )


def create_main_parser(
    prog_name: str = "claude-mpm", version: str = "0.0.0"
) -> argparse.ArgumentParser:
    """
    Create the main argument parser with basic setup.

    WHY: This creates the foundation parser that other modules will extend
    with their specific subcommands and arguments.

    DESIGN DECISION: Uses SuggestingArgumentParser to provide helpful suggestions
    for typos and invalid commands, improving user experience.

    Args:
        prog_name: The program name to use
        version: The version string to display

    Returns:
        Configured SuggestingArgumentParser instance ready for subparser addition
    """
    # Main parser with suggestion support
    parser = SuggestingArgumentParser(
        prog=prog_name,
        description=f"Claude Multi-Agent Project Manager v{version} - Orchestrate Claude with agent delegation and ticket tracking",
        epilog="By default, runs an orchestrated Claude session. Use 'claude-mpm' for interactive mode or 'claude-mpm -i \"prompt\"' for non-interactive mode.\n\nTo pass arguments to Claude CLI, use -- separator: claude-mpm run -- --model sonnet --temperature 0.1",
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )

    # Add common arguments to main parser with version
    add_common_arguments(parser, version=version)

    return parser


def add_top_level_run_arguments(parser: argparse.ArgumentParser) -> None:
    """
    Add run-specific arguments at top level for backward compatibility.

    WHY: This maintains backward compatibility - users can run `claude-mpm -i "prompt"`
    without specifying the 'run' command.

    Args:
        parser: The argument parser to add arguments to
    """
    # Add run-specific arguments at top level for default behavior
    # NOTE: We don't add claude_args here because REMAINDER interferes with subcommands
    run_group = parser.add_argument_group("run options (when no command specified)")

    run_group.add_argument(
        "--no-hooks",
        action="store_true",
        help="Disable hook service (runs without hooks)",
    )
    run_group.add_argument(
        "--no-tickets", action="store_true", help="Disable automatic ticket creation"
    )
    run_group.add_argument(
        "--intercept-commands",
        action="store_true",
        help="Enable command interception in interactive mode (intercepts /mpm: commands)",
    )
    run_group.add_argument(
        "--no-native-agents",
        action="store_true",
        help="Disable deployment of Claude Code native agents",
    )
    run_group.add_argument(
        "--launch-method",
        choices=["exec", "subprocess"],
        default="exec",
        help="Method to launch Claude: exec (replace process) or subprocess (child process)",
    )
    # Monitor options - consolidated monitoring and management interface
    run_group.add_argument(
        "--monitor",
        action="store_true",
        help="Enable monitoring and management interface with WebSocket server and dashboard (default port: 8765)",
    )
    run_group.add_argument(
        "--websocket-port",
        type=int,
        default=8765,
        help="WebSocket server port (default: 8765)",
    )
    run_group.add_argument(
        "--mpm-resume",
        type=str,
        nargs="?",
        const="last",
        help="Resume an MPM session (last session if no ID specified, or specific session ID)",
    )
    run_group.add_argument(
        "--resume",
        type=str,
        nargs="?",
        const="",
        default=None,
        help="Resume a Claude Code session. Without argument: resume last session. With session_id: resume specific session",
    )
    run_group.add_argument(
        "--force",
        action="store_true",
        help="Force operations even with warnings (e.g., large .claude.json file)",
    )
    run_group.add_argument(
        "--reload-agents",
        action="store_true",
        help="Force rebuild of all system agents by deleting local claude-mpm agents",
    )
    run_group.add_argument(
        "--force-sync",
        action="store_true",
        help="Force refresh agents and skills from remote repos, bypassing ETag cache",
    )
    run_group.add_argument(
        "--no-sync",
        action="store_true",
        dest="no_sync",
        default=False,
        help="Skip agent and skills sync for faster startup (uses existing cached files)",
    )
    run_group.add_argument(
        "--skip-compat-check",
        action="store_true",
        default=False,
        help="Skip manifest compatibility checking during agent sync",
    )
    run_group.add_argument(
        "--chrome",
        action="store_true",
        help="Enable Claude in Chrome integration (passed to Claude Code)",
    )
    run_group.add_argument(
        "--no-chrome",
        action="store_true",
        help="Disable Claude in Chrome integration (passed to Claude Code)",
    )
    run_group.add_argument(
        "--slack",
        action="store_true",
        help="Start the Slack MPM bot (requires SLACK_BOT_TOKEN and SLACK_APP_TOKEN)",
    )
    run_group.add_argument(
        "--mcp",
        type=str,
        metavar="SERVICES",
        help="Comma-separated list of MCP services to enable for this session (e.g., --mcp kuzu-memory,mcp-ticketer,gworkspace-mcp). Use 'claude-mpm mcp list' to see available services.",
    )
    run_group.add_argument(
        "--no-dangerously-skip-permissions",
        action="store_true",
        dest="no_dangerously_skip_permissions",
        help="Disable the --dangerously-skip-permissions flag passed to Claude Code subprocesses. "
        "Use in security-sensitive environments (CI/CD, DevOps, SRE). "
        "Also controlled by CLAUDE_MPM_NO_SKIP_PERMISSIONS=1 env var.",
    )

    # Dependency checking options (for backward compatibility at top level)
    dep_group_top = parser.add_argument_group(
        "dependency options (when no command specified)"
    )
    dep_group_top.add_argument(
        "--no-check-dependencies",
        action="store_false",
        dest="check_dependencies",
        help="Skip agent dependency checking at startup",
    )
    dep_group_top.add_argument(
        "--force-check-dependencies",
        action="store_true",
        help="Force dependency checking even if cached results exist",
    )
    dep_group_top.add_argument(
        "--no-prompt",
        action="store_true",
        help="Never prompt for dependency installation (non-interactive mode)",
    )
    dep_group_top.add_argument(
        "--force-prompt",
        action="store_true",
        help="Force interactive prompting even in non-TTY environments (use with caution)",
    )

    # Input/output options
    io_group = parser.add_argument_group(
        "input/output options (when no command specified)"
    )
    io_group.add_argument(
        "-i",
        "--input",
        type=str,
        help="Input text or file path (for non-interactive mode)",
    )
    io_group.add_argument(
        "--non-interactive",
        action="store_true",
        help="Run in non-interactive mode (read from stdin or --input)",
    )
    io_group.add_argument(
        "--headless",
        action="store_true",
        help="Run in headless mode (disables Rich console, uses stream-json output for programmatic use)",
    )


def create_parser(
    prog_name: str = "claude-mpm", version: str = "0.0.0"
) -> argparse.ArgumentParser:
    """
    Create the main argument parser with all subcommands.

    WHY: This factory function creates a complete parser with all commands and their
    arguments. It's the single entry point for creating the CLI parser, ensuring
    consistency across the application.

    DESIGN DECISION: We use subparsers for commands to provide a clean, git-like
    interface while maintaining backward compatibility with the original CLI.

    Args:
        prog_name: The program name to use
        version: The version string to display

    Returns:
        Configured ArgumentParser instance
    """
    # Create main parser
    parser = create_main_parser(prog_name, version)

    # Add top-level run arguments for backward compatibility
    add_top_level_run_arguments(parser)

    # Create subparsers for commands
    subparsers = parser.add_subparsers(
        dest="command", help="Available commands", metavar="COMMAND"
    )

    # Import and add core subparsers one by one to avoid issues
    try:
        from .run_parser import add_run_subparser

        add_run_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .tickets_parser import add_tickets_subparser

        add_tickets_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .agents_parser import add_agents_subparser

        add_agents_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .source_parser import add_source_subparser

        add_source_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .skill_source_parser import add_skill_source_subparser

        add_skill_source_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .agent_source_parser import add_agent_source_subparser

        add_agent_source_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .auto_configure_parser import add_auto_configure_subparser

        add_auto_configure_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .memory_parser import add_memory_subparser

        add_memory_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .skills_parser import add_skills_subparser

        add_skills_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .messages_parser import add_messages_subparser

        add_messages_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .queue_parser import add_queue_subparser

        add_queue_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .config_parser import add_config_subparser

        add_config_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .profile_parser import add_profile_subparser

        add_profile_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .monitor_parser import add_monitor_subparser

        add_monitor_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .dashboard_parser import add_dashboard_subparser

        add_dashboard_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .local_deploy_parser import add_local_deploy_arguments

        add_local_deploy_arguments(subparsers)
    except ImportError:
        pass

    try:
        from .mcp_parser import add_mcp_subparser

        add_mcp_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .agent_manager_parser import add_agent_manager_subparser

        add_agent_manager_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .configure_parser import add_configure_subparser

        add_configure_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .oauth_parser import add_oauth_subparser

        add_oauth_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .auth_parser import add_auth_subparser

        add_auth_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .setup_parser import add_setup_subparser

        add_setup_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .install_parser import add_install_subparser

        add_install_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .slack_parser import add_slack_subparser

        add_slack_subparser(subparsers)
    except ImportError:
        pass

    try:
        from .tools_parser import add_tools_subparser

        add_tools_subparser(subparsers)
    except ImportError:
        pass

    # Add provider command parser for API backend management
    try:
        from .provider_parser import add_provider_subparser

        add_provider_subparser(subparsers)
    except ImportError:
        pass

    # Add uninstall command parser
    try:
        from ..commands.uninstall import add_uninstall_parser

        add_uninstall_parser(subparsers)
    except ImportError:
        pass

    # Add debug command parser
    try:
        from .debug_parser import add_debug_subparser

        add_debug_subparser(subparsers)
    except ImportError:
        pass

    # Add analyze command parser
    try:
        from .analyze_parser import add_analyze_subparser

        add_analyze_subparser(subparsers)
    except ImportError:
        pass

    # Add analyze-code command parser
    try:
        from .analyze_code_parser import AnalyzeCodeParser

        parser_obj = AnalyzeCodeParser()
        analyze_code_parser = subparsers.add_parser(
            "analyze-code", help=parser_obj.help_text
        )
        parser_obj.add_arguments(analyze_code_parser)
        analyze_code_parser.set_defaults(command="analyze-code")
    except ImportError:
        pass

    # Add mpm-init command parser
    try:
        from .mpm_init_parser import add_mpm_init_subparser

        add_mpm_init_subparser(subparsers)
    except ImportError:
        pass

    # Add search command parser
    try:
        from .search_parser import add_search_subparser

        add_search_subparser(subparsers)
    except ImportError:
        pass

    # Import and add additional command parsers from commands module
    try:
        from ..commands.aggregate import add_aggregate_parser

        add_aggregate_parser(subparsers)

        from ..commands.cleanup import add_cleanup_parser

        add_cleanup_parser(subparsers)

        # MCP pipx configuration command
        if hasattr(CLICommands, "MCP_PIPX_CONFIG") or True:  # Always add for now
            from ..commands.mcp_pipx_config import add_parser as add_mcp_pipx_parser

            add_mcp_pipx_parser(subparsers)

        from ..commands.doctor import add_doctor_parser

        add_doctor_parser(subparsers)

        from ..commands.gh import add_gh_parser

        add_gh_parser(subparsers)

        from ..commands.postmortem import add_postmortem_parser

        add_postmortem_parser(subparsers)

        # Add upgrade command
        from ..commands.upgrade import add_upgrade_parser

        add_upgrade_parser(subparsers)

        # Add verify command for MCP service verification
        from ..commands.verify import add_parser as add_verify_parser

        add_verify_parser(subparsers)

        # Add hook-errors command for managing hook error memory
        hook_errors_parser = subparsers.add_parser(
            "hook-errors",
            help="Manage hook error memory and diagnostics",
        )
        hook_errors_parser.add_argument(
            "hook_errors_command",
            nargs="?",
            choices=["list", "summary", "clear", "diagnose", "status"],
            help="Hook errors subcommand",
        )
        hook_errors_parser.add_argument(
            "--format",
            choices=["table", "json"],
            default="table",
            help="Output format for list command",
        )
        hook_errors_parser.add_argument(
            "--hook-type",
            help="Filter by specific hook type",
        )
        hook_errors_parser.add_argument(
            "-y",
            "--yes",
            action="store_true",
            help="Skip confirmation prompts",
        )

        # Add autotodos command for auto-generating todos from hook errors
        autotodos_parser = subparsers.add_parser(
            "autotodos",
            help="Auto-generate todos from hook errors and delegation patterns",
        )
        autotodos_parser.add_argument(
            "autotodos_command",
            nargs="?",
            choices=["list", "inject", "clear", "status", "scan", "violations"],
            help="AutoTodos subcommand",
        )
        autotodos_parser.add_argument(
            "text",
            nargs="?",
            help="Text to scan for delegation patterns (scan command only)",
        )
        autotodos_parser.add_argument(
            "--format",
            choices=["table", "json"],
            default="table",
            help="Output format for list/scan commands",
        )
        autotodos_parser.add_argument(
            "--output",
            help="Output file path for inject command",
        )
        autotodos_parser.add_argument(
            "--error-key",
            help="Specific error key to clear",
        )
        autotodos_parser.add_argument(
            "--event-type",
            choices=["error", "violation", "all"],
            default="all",
            help="Type of events to clear (clear command only)",
        )
        autotodos_parser.add_argument(
            "--file",
            "-f",
            help="Scan text from file (scan command only)",
        )
        autotodos_parser.add_argument(
            "--save",
            action="store_true",
            help="Save detections to event log (scan command only)",
        )
        autotodos_parser.add_argument(
            "-y",
            "--yes",
            action="store_true",
            help="Skip confirmation prompts",
        )

        # Add summarize command
        from ..commands.summarize import add_summarize_parser

        add_summarize_parser(subparsers)
    except ImportError:
        # Commands module may not be available during testing or refactoring
        pass

    return parser


def preprocess_args(argv: list[str] | None = None) -> list[str]:
    """
    Preprocess arguments to handle --mpm: prefix commands.

    WHY: We support both --mpm:command and regular command syntax for flexibility
    and backward compatibility. This function normalizes the input.

    Args:
        argv: List of command line arguments, or None to use sys.argv[1:]

    Returns:
        Processed list of arguments with prefixes removed
    """
    import sys

    if argv is None:
        argv = sys.argv[1:]

    # Convert --mpm:command to command for argparse compatibility
    processed_args = []
    for arg in argv:
        if arg.startswith(CLIPrefix.MPM.value):
            # Extract command after prefix
            command = arg[len(CLIPrefix.MPM.value) :]
            processed_args.append(command)
        else:
            processed_args.append(arg)

    return processed_args
