"""MakePython: Automated Python project building and management tool.

This module provides a command-line interface for common Python development tasks
using a modern command architecture with clear separation of concerns.

Features:
    - Modern command architecture with clear separation of concerns
    - Centralized command registration and management
    - Standardized execution context and validation
    - Support for multiple build tools (uv, poetry, hatch)
    - Comprehensive error handling and logging
    - Structured command results with detailed feedback

Example:
    >>> # Build project
    >>> makepython build
    >>>
    >>> # Run tests
    >>> makepython test
    >>>
    >>> # Publish to PyPI
    >>> makepython publish
"""

from __future__ import annotations

import argparse
import logging
import sys

from pytola.core.logger import get_logger

from ._config import MakePythonConfig, __version__
from .core.context import create_default_context
from .core.factory import initialize_command_system
from .core.registry import get_registry

logger = get_logger(__name__)


def main() -> None:
    """Run main entry point for MakePython tool.

    Loads all commands, parses arguments, and executes the requested command.

    Raises
    ------
        SystemExit: If command execution fails or unknown command is provided
    """
    try:
        # Initialize command system
        initialize_command_system()

        # Get registry
        registry = get_registry()

        # Get all available command names (including aliases)
        all_names = registry.list_commands() + list(registry.list_aliases().keys())

        if not all_names:
            logger.error("No commands loaded!")
            sys.exit(1)

        # Parse arguments
        parser = argparse.ArgumentParser(
            prog="makepython",
            description="Automated Python project building and management tool",
            epilog="""Examples:
  makepython build           Build the project
  makepython test            Run tests
  makepython publish         Publish to PyPI
  makepython clean           Clean build artifacts

Use 'makepython --help' to see all available commands.""",
            formatter_class=argparse.RawDescriptionHelpFormatter,
        )

        # Add argument groups for better organization
        info_group = parser.add_argument_group("Information Options")
        info_group.add_argument(
            "--version",
            action="version",
            version=f"MakePython v{__version__}",
            help="Show version information and exit",
        )
        info_group.add_argument(
            "--list-commands",
            "-lc",
            action="store_true",
            help="List all available commands with descriptions",
        )
        info_group.add_argument(
            "--help-commands",
            "-hc",
            action="store_true",
            help="Show detailed help for all commands",
        )

        logging_group = parser.add_argument_group("Logging Options")
        logging_group.add_argument(
            "--debug",
            "-d",
            action="store_true",
            help="Enable debug mode with verbose output",
        )

        parser.add_argument(
            "command",
            nargs="?",
            type=str,
            choices=all_names,
            help=f"Command to run. Available: {', '.join(sorted(all_names))}",
        )

        parser.add_argument(
            "args",
            nargs=argparse.REMAINDER,
            help="Additional arguments for the command",
        )

        args = parser.parse_args()

        # Handle information options
        if args.list_commands:
            _list_commands()
            return

        if args.help_commands:
            _show_detailed_help()
            return

        # Set logging level
        config = MakePythonConfig()
        if args.debug:
            logger.setLevel(logging.DEBUG)
            config.verbose_output = True

        # Validate that a command was provided
        if not args.command:
            parser.print_help()
            logger.error("No command specified. Use --help for usage information.")
            sys.exit(1)

        # Execute command
        logger.info(f"Executing command: {args.command}")
        _execute_command(args.command, args.args)

    except KeyboardInterrupt:
        logger.info("Operation cancelled by user")
        sys.exit(130)  # Standard exit code for Ctrl+C
    except Exception as e:
        logger.exception(f"Unexpected error: {e}")
        sys.exit(1)


def _execute_command(command_name: str, command_args: list[str]) -> None:
    """Execute a registered command.

    Args:
        command_name: Name or alias of the command to execute
        command_args: Additional arguments for the command
    """
    registry = get_registry()

    # Create execution context
    context = create_default_context()

    # Check if command exists before executing
    command_cls = registry.get_command_class(command_name)

    if not command_cls:
        logger.error(f"Unknown command: {command_name}")
        available_commands = registry.list_commands() + list(registry.list_aliases().keys())
        logger.info(f"Available commands: {', '.join(sorted(available_commands))}")
        sys.exit(1)

    # Execute command through registry
    result = registry.execute_command(command_name, context)

    if not result.success:
        logger.error(f"Command execution failed: {result.message}")
        sys.exit(1)

    # Command succeeded
    logger.info("Command completed successfully!")


def _list_commands() -> None:
    """List all available commands with their descriptions."""
    print("Available MakePython Commands:")
    print("=" * 50)

    registry = get_registry()

    # Get all command types
    all_types = registry.get_all_types()

    for cmd_type in all_types:
        commands = registry.get_commands_by_type(cmd_type)
        if commands:  # Only show types that have commands
            print(f"\n{cmd_type.name.title()} Commands:")
            print("-" * 30)
            for cmd_name in sorted(commands):
                # Get command class to access description
                cmd_cls = registry.get_command_class(cmd_name)
                if cmd_cls:
                    description = getattr(cmd_cls, "description", "No description")
                    alias = getattr(cmd_cls, "alias", "No alias")
                    print(f"  {cmd_name} ({alias}) - {description}")


def _show_detailed_help() -> None:
    """Show detailed help for all commands."""
    registry = get_registry()

    print("Detailed Help for MakePython Commands:\n")

    # Get all command types
    all_types = registry.get_all_types()

    # Define type order for consistent output
    from .interfaces.command import CommandType

    type_order = [
        CommandType.BUILD,
        CommandType.TEST,
        CommandType.PUBLISH,
        CommandType.BUMP_VERSION,
        CommandType.TOKEN,
        CommandType.CLEAN,
        CommandType.UTIL,
    ]

    # Show commands in ordered types
    for cmd_type in type_order:
        if cmd_type in all_types:
            commands = registry.get_commands_by_type(cmd_type)
            if commands:
                print(f"{cmd_type.name.title()} Commands:")
                for cmd_name in sorted(commands):
                    cmd_cls = registry.get_command_class(cmd_name)
                    if cmd_cls:
                        description = getattr(cmd_cls, "description", "No description")
                        alias = getattr(cmd_cls, "alias", "No alias")
                        print(f"  {cmd_name} ({alias}) - {description}")
                print("")

    # Show any remaining types not in the predefined order
    for cmd_type in all_types:
        if cmd_type not in type_order:
            commands = registry.get_commands_by_type(cmd_type)
            if commands:
                print(f"{cmd_type.name.title()} Commands:")
                for cmd_name in sorted(commands):
                    cmd_cls = registry.get_command_class(cmd_name)
                    if cmd_cls:
                        description = getattr(cmd_cls, "description", "No description")
                        alias = getattr(cmd_cls, "alias", "No alias")
                        print(f"  {cmd_name} ({alias}) - {description}")
                print("")


if __name__ == "__main__":
    main()
