import re
import shlex
from typing import Optional, Dict, Any, List, Callable, Union
from enum import Enum
from dataclasses import dataclass


class ArgumentType(Enum):
    FLAG = 1
    VALUE = 2
    KEY_VALUE = 3


@dataclass
class Argument:
    name: str
    type: ArgumentType
    aliases: List[str] = None
    required: bool = False
    default: Any = None
    description: str = ""
    validator: Callable = None

    def __post_init__(self):
        if self.aliases is None:
            self.aliases = []


class CommandBase:
    def __init__(self, name: str, description: str = "", usage: str = "") -> None:
        self.name: str = name
        self.description: str = description
        self.usage: Union[str, List[str]] = usage
        self.arguments: List[Argument] = []
        self.core: Optional['AdvancedArgParser'] = None

    def add_argument(self, name: str, arg_type: ArgumentType, **kwargs):
        """Добавить аргумент команды"""
        aliases = kwargs.get('aliases', [])
        argument = Argument(
            name=name,
            type=arg_type,
            aliases=aliases,
            required=kwargs.get('required', False),
            default=kwargs.get('default'),
            description=kwargs.get('description', ""),
            validator=kwargs.get('validator')
        )
        self.arguments.append(argument)

    def run(self, parsed_args: Dict[str, Any]) -> int:
        raise NotImplementedError("Commands must implement run method")

    def validate_arguments(self, args: Dict[str, Any]) -> bool:
        """Валидация аргументов команды"""
        for arg_def in self.arguments:
            if arg_def.required and arg_def.name not in args:
                print(f"Missing required argument: {arg_def.name}")
                return False
            if arg_def.validator and arg_def.name in args:
                if not arg_def.validator(args[arg_def.name]):
                    print(f"Validation failed for argument: {arg_def.name}")
                    return False
        return True

    def print_help(self):
        """Вывод справки по команде"""
        print(f"{bcolors.BOLD}{self.name}{bcolors.ENDC} - {self.description}")

        if isinstance(self.usage, list):
            print("Usage:")
            for usage in self.usage:
                print(f"  $ {usage}")
        else:
            print(f"Usage: $ {self.usage}")

        if self.arguments:
            print("\nArguments:")
            for arg in self.arguments:
                req_str = " (required)" if arg.required else ""
                alias_str = ", ".join([f"-{a}" for a in arg.aliases]) if arg.aliases else ""
                alias_str = f" [{alias_str}]" if alias_str else ""

                print(f"  {arg.name}{alias_str}{req_str}")
                if arg.description:
                    print(f"    {arg.description}")
                if arg.default is not None:
                    print(f"    Default: {arg.default}")


class HelpCommand(CommandBase):
    def __init__(self, core):
        super().__init__(
            name='help',
            description="Print information about commands",
            usage=f"{core.app_binname} help [command]"
        )
        self.core = core
        self.add_argument('command', ArgumentType.VALUE, required=False,
                          description="Command name to get detailed help")

    def run(self, parsed_args: Dict[str, Any]) -> int:
        command_name = parsed_args.get('command')

        if command_name:
            command = self.core.get_command(command_name)
            if command:
                command.print_help()
            else:
                print(f"Command '{command_name}' not found!")
                print("Available commands:")
                for cmd_name in self.core.registered_commands.keys():
                    if cmd_name != 'help':
                        print(f"  {cmd_name}")
                return 1
        else:
            self._print_general_help()
        return 0

    def _print_general_help(self):
        print(f"{self.core.app_displayname} Commands:\n")

        for cmd_name, command in self.core.registered_commands.items():
            if cmd_name == 'help':
                continue

            usage = self._format_usage(command.usage)
            print(f"  {bcolors.BOLD}{bcolors.OKCYAN}{command.name}{bcolors.ENDC}")
            print(f"    {command.description}")
            print(f"    Usage: {usage}\n")

    def _format_usage(self, usage: Union[str, List[str]]) -> str:
        if isinstance(usage, list):
            return '\n    '.join([f"$ {u}" for u in usage])
        return f"$ {usage}"


class AdvancedArgParser:
    def __init__(self, app_id: str, app_displayname: str, app_binname: str):
        self.app_id = app_id
        self.app_binname = app_binname
        self.app_displayname = app_displayname

        self.registered_commands: Dict[str, CommandBase] = {}
        self.default_command: Optional[str] = None

        # Регистрируем help команду
        self.register_command(HelpCommand(self))

    def register_command(self, command: CommandBase):
        """Регистрация команды"""
        command.core = self
        self.registered_commands[command.name] = command

    def set_default_command(self, command_name: str):
        """Установка команды по умолчанию"""
        if command_name in self.registered_commands:
            self.default_command = command_name
        else:
            print(f"Warning: Command '{command_name}' not found, cannot set as default")

    def get_command(self, name: str) -> Optional[CommandBase]:
        """Получить команду по имени"""
        return self.registered_commands.get(name)

    def parse_arguments(self, args: List[str]) -> Dict[str, Any]:
        """
        Парсинг аргументов с поддержкой различных форматов
        """
        if not args:
            return {'command': self.default_command} if self.default_command else {}

        parsed = {
            'command': None,
            'flags': set(),
            'values': [],
            'key_values': {}
        }

        i = 0
        while i < len(args):
            arg = args[i].strip()

            if not arg:  # Пропускаем пустые аргументы
                i += 1
                continue

            if arg.startswith('--'):
                # Длинные аргументы: --flag, --key=value
                arg_content = arg[2:]
                if '=' in arg_content:
                    key, value = arg_content.split('=', 1)
                    parsed['key_values'][key] = self._parse_value(value)
                else:
                    parsed['flags'].add(arg_content)
                i += 1

            elif arg.startswith('-'):
                # Короткие аргументы: -f, -abc, -k=value
                arg_content = arg[1:]
                if not arg_content:  # Одиночный дефис
                    parsed['values'].append(arg)
                    i += 1
                    continue

                if '=' in arg_content:
                    # Формат: -k=value
                    key, value = arg_content.split('=', 1)
                    parsed['key_values'][key] = self._parse_value(value)
                    i += 1
                elif len(arg_content) > 1:
                    # Комбинированные флаги: -abc
                    for char in arg_content:
                        parsed['flags'].add(char)
                    i += 1
                else:
                    # Одиночный флаг или флаг со значением
                    flag_name = arg_content
                    if i + 1 < len(args) and not args[i + 1].startswith('-'):
                        # Флаг со значением: -f value
                        parsed['key_values'][flag_name] = self._parse_value(args[i + 1])
                        i += 2
                    else:
                        # Одиночный флаг
                        parsed['flags'].add(flag_name)
                        i += 1
            else:
                # Позиционные аргументы
                if parsed['command'] is None:
                    parsed['command'] = arg
                else:
                    parsed['values'].append(self._parse_value(arg))
                i += 1

        return parsed

    def _parse_value(self, value: str) -> Any:
        """Парсинг значений с автоматическим определением типа"""
        if not value:
            return value

        if value.lower() in ('true', 'false'):
            return value.lower() == 'true'
        elif value.isdigit():
            return int(value)
        try:
            return float(value)
        except ValueError:
            pass
        # Убираем кавычки если есть
        if len(value) > 1 and value[0] == value[-1] and value[0] in ('"', "'"):
            return value[1:-1]
        return value

    def execute(self, raw_args: List[str]) -> int:
        """Выполнение парсинга и запуск команды"""
        try:
            # Если raw_args - строка, разбиваем ее
            if isinstance(raw_args, str):
                args = shlex.split(raw_args)
            else:
                args = [str(arg) for arg in raw_args if arg is not None]
        except Exception as e:
            print(f"Error parsing arguments: {e}")
            return 1

        if not args:
            if self.default_command:
                args = [self.default_command]
            else:
                print("No arguments provided and no default command set!")
                return 1

        parsed_args = self.parse_arguments(args)
        command_name = parsed_args.get('command', self.default_command)

        if not command_name:
            print("No command specified!")
            self._print_help()
            return 1

        command = self.get_command(command_name)
        if not command:
            print(f"Command '{command_name}' not found!")
            print("Available commands:", ", ".join(self.registered_commands.keys()))
            return 1

        # Нормализуем аргументы для команды
        normalized_args = self._normalize_arguments(parsed_args, command)

        # Добавляем command в normalized_args для доступа в run методе
        normalized_args['command'] = command_name

        if not command.validate_arguments(normalized_args):
            print(f"Invalid arguments for command '{command_name}'")
            command.print_help()
            return 1

        try:
            return command.run(normalized_args)
        except Exception as e:
            print(f"Error executing command '{command_name}': {e}")
            return 1

    def _normalize_arguments(self, parsed_args: Dict[str, Any], command: CommandBase) -> Dict[str, Any]:
        """Нормализация аргументов для команды"""
        normalized = {}

        # Обрабатываем флаги
        for flag in parsed_args['flags']:
            normalized[flag] = True

        # Обрабатываем ключ-значение
        normalized.update(parsed_args['key_values'])

        # Обрабатываем позиционные аргументы
        for i, value in enumerate(parsed_args['values']):
            normalized[f'arg_{i}'] = value

        return normalized

    def _print_help(self):
        """Показать общую справку"""
        help_cmd = self.get_command('help')
        if help_cmd:
            help_cmd.run({'command': None})


# Исправленный ExampleCommand
class ExampleCommand(CommandBase):
    def __init__(self):
        super().__init__(
            name='example',
            description='Example command with various argument types',
            usage=['app example --verbose input.txt',
                   'app example -v -c=config.json file1 file2']
        )

        # Добавляем аргументы
        self.add_argument('verbose', ArgumentType.FLAG,
                          aliases=['v'],
                          description='Enable verbose output')

        self.add_argument('config', ArgumentType.KEY_VALUE,
                          aliases=['c'],
                          description='Configuration file')

        self.add_argument('input', ArgumentType.VALUE,
                          required=False,  # Сделаем необязательным для теста
                          description='Input file')

    def run(self, parsed_args: Dict[str, Any]) -> int:
        print("Executing example command with arguments:")
        for key, value in parsed_args.items():
            print(f"  {key}: {value}")
        return 0


# Цвета для вывода
class bcolors:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKCYAN = '\033[96m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'


# Пример использования с исправлением
if __name__ == "__main__":
    parser = AdvancedArgParser("myapp", "My Application", "myapp")
    parser.register_command(ExampleCommand())

    # Тестируем ваш пример
    result = parser.execute(['example', '--verbose', 'input.txt', '-c=config.json'])
    print(f"Result: {result}")