"""
Argument parsing utilities for terminal client commands.

Provides robust parsing for both REPL and CLI commands using shlex
to handle quoted strings, spaces, and complex arguments.

Author: Claude
Date: 2025-10-27
"""
import shlex
import logging
from typing import Dict, Any, List, Optional, Tuple

logger = logging.getLogger(f'{__name__}.arg_parser')


class ArgumentParseError(Exception):
    """Raised when argument parsing fails."""
    pass


class CommandArgumentParser:
    """Parse command-line style arguments for terminal client."""

    @staticmethod
    def parse_args(arg_string: str, schema: Dict[str, Any]) -> Dict[str, Any]:
        """
        Parse argument string according to schema.

        Args:
            arg_string: Raw argument string from command
            schema: Dictionary defining expected arguments
                {
                    'league': {'type': str, 'default': 'sba'},
                    'home_team': {'type': int, 'default': 1},
                    'count': {'type': int, 'default': 1, 'positional': True},
                    'verbose': {'type': bool, 'flag': True}
                }

        Returns:
            Dictionary of parsed arguments with defaults applied

        Raises:
            ArgumentParseError: If parsing fails or validation fails
        """
        try:
            # Use shlex for robust parsing
            tokens = shlex.split(arg_string) if arg_string.strip() else []
        except ValueError as e:
            raise ArgumentParseError(f"Invalid argument syntax: {e}")

        # Initialize result with defaults
        result = {}
        for key, spec in schema.items():
            if 'default' in spec:
                result[key] = spec['default']

        # Track which positional arg we're on
        positional_keys = [k for k, v in schema.items() if v.get('positional', False)]
        positional_index = 0

        i = 0
        while i < len(tokens):
            token = tokens[i]

            # Handle flags (--option or -o)
            if token.startswith('--'):
                option_name = token[2:]

                # Convert hyphen to underscore for Python compatibility
                option_key = option_name.replace('-', '_')

                if option_key not in schema:
                    raise ArgumentParseError(f"Unknown option: {token}")

                spec = schema[option_key]

                # Boolean flags don't need a value
                if spec.get('flag', False):
                    result[option_key] = True
                    i += 1
                    continue

                # Option requires a value
                if i + 1 >= len(tokens):
                    raise ArgumentParseError(f"Option {token} requires a value")

                value_str = tokens[i + 1]

                # Type conversion
                try:
                    if spec['type'] == int:
                        result[option_key] = int(value_str)
                    elif spec['type'] == float:
                        result[option_key] = float(value_str)
                    elif spec['type'] == list:
                        # Parse comma-separated list
                        result[option_key] = [item.strip() for item in value_str.split(',')]
                    elif spec['type'] == 'int_list':
                        # Parse comma-separated integers
                        result[option_key] = [int(item.strip()) for item in value_str.split(',')]
                    else:
                        result[option_key] = value_str
                except ValueError as e:
                    raise ArgumentParseError(
                        f"Invalid value for {token}: expected {spec['type'].__name__ if hasattr(spec['type'], '__name__') else spec['type']}, got '{value_str}'"
                    )

                i += 2

            # Handle positional arguments
            else:
                if positional_index >= len(positional_keys):
                    raise ArgumentParseError(f"Unexpected positional argument: {token}")

                key = positional_keys[positional_index]
                spec = schema[key]

                try:
                    if spec['type'] == int:
                        result[key] = int(token)
                    elif spec['type'] == float:
                        result[key] = float(token)
                    else:
                        result[key] = token
                except ValueError as e:
                    raise ArgumentParseError(
                        f"Invalid value for {key}: expected {spec['type'].__name__}, got '{token}'"
                    )

                positional_index += 1
                i += 1

        return result

    @staticmethod
    def parse_game_id(arg_string: str) -> Optional[str]:
        """
        Parse a game ID from argument string.

        Args:
            arg_string: Raw argument string

        Returns:
            Game ID string or None
        """
        try:
            tokens = shlex.split(arg_string) if arg_string.strip() else []

            # Look for --game-id option
            for i, token in enumerate(tokens):
                if token == '--game-id' and i + 1 < len(tokens):
                    return tokens[i + 1]

            # If no option, check if there's a positional UUID-like argument
            if tokens and len(tokens[0]) == 36:  # UUID length
                return tokens[0]

            return None

        except ValueError:
            return None


# Predefined schemas for common commands
NEW_GAME_SCHEMA = {
    'league': {'type': str, 'default': 'sba'},
    'home_team': {'type': int, 'default': 1},
    'away_team': {'type': int, 'default': 2}
}

DEFENSIVE_SCHEMA = {
    'alignment': {'type': str, 'default': 'normal'},
    'infield': {'type': str, 'default': 'normal'},
    'outfield': {'type': str, 'default': 'normal'},
    'hold': {'type': 'int_list', 'default': []}
}

OFFENSIVE_SCHEMA = {
    'action': {'type': str, 'default': 'swing_away'},  # Session 2: changed from approach
    'steal': {'type': 'int_list', 'default': []}
}

QUICK_PLAY_SCHEMA = {
    'count': {'type': int, 'default': 1, 'positional': True}
}

USE_GAME_SCHEMA = {
    'game_id': {'type': str, 'positional': True}
}


def parse_new_game_args(arg_string: str) -> Dict[str, Any]:
    """Parse arguments for new_game command."""
    return CommandArgumentParser.parse_args(arg_string, NEW_GAME_SCHEMA)


def parse_defensive_args(arg_string: str) -> Dict[str, Any]:
    """Parse arguments for defensive command."""
    return CommandArgumentParser.parse_args(arg_string, DEFENSIVE_SCHEMA)


def parse_offensive_args(arg_string: str) -> Dict[str, Any]:
    """Parse arguments for offensive command."""
    return CommandArgumentParser.parse_args(arg_string, OFFENSIVE_SCHEMA)


def parse_quick_play_args(arg_string: str) -> Dict[str, Any]:
    """Parse arguments for quick_play command."""
    return CommandArgumentParser.parse_args(arg_string, QUICK_PLAY_SCHEMA)


def parse_use_game_args(arg_string: str) -> Dict[str, Any]:
    """Parse arguments for use_game command."""
    return CommandArgumentParser.parse_args(arg_string, USE_GAME_SCHEMA)
