"""
Unit tests for argument parser.
"""
import pytest

from terminal_client.arg_parser import (
    CommandArgumentParser,
    ArgumentParseError,
    parse_new_game_args,
    parse_defensive_args,
    parse_offensive_args,
    parse_quick_play_args,
    parse_use_game_args
)


class TestCommandArgumentParser:
    """Tests for CommandArgumentParser."""

    def test_parse_simple_string_arg(self):
        """Test parsing simple string argument."""
        schema = {'name': {'type': str, 'default': 'default'}}
        result = CommandArgumentParser.parse_args('--name test', schema)
        assert result['name'] == 'test'

    def test_parse_integer_arg(self):
        """Test parsing integer argument."""
        schema = {'count': {'type': int, 'default': 1}}
        result = CommandArgumentParser.parse_args('--count 42', schema)
        assert result['count'] == 42

    def test_parse_flag_arg(self):
        """Test parsing boolean flag."""
        schema = {
            'verbose': {'type': bool, 'flag': True, 'default': False}
        }
        result = CommandArgumentParser.parse_args('--verbose', schema)
        assert result['verbose'] is True

    def test_parse_int_list(self):
        """Test parsing comma-separated integer list."""
        schema = {'bases': {'type': 'int_list', 'default': []}}
        result = CommandArgumentParser.parse_args('--bases 1,2,3', schema)
        assert result['bases'] == [1, 2, 3]

    def test_parse_quoted_string(self):
        """Test parsing quoted string with spaces."""
        schema = {'message': {'type': str, 'default': ''}}
        result = CommandArgumentParser.parse_args('--message "hello world"', schema)
        assert result['message'] == 'hello world'

    def test_parse_positional_arg(self):
        """Test parsing positional argument."""
        schema = {
            'count': {'type': int, 'positional': True, 'default': 1}
        }
        result = CommandArgumentParser.parse_args('10', schema)
        assert result['count'] == 10

    def test_parse_mixed_args(self):
        """Test parsing mix of options and positional."""
        schema = {
            'count': {'type': int, 'positional': True, 'default': 1},
            'league': {'type': str, 'default': 'sba'}
        }
        result = CommandArgumentParser.parse_args('5 --league pd', schema)
        assert result['count'] == 5
        assert result['league'] == 'pd'

    def test_parse_unknown_option_raises(self):
        """Test that unknown option raises error."""
        schema = {'name': {'type': str, 'default': 'default'}}
        with pytest.raises(ArgumentParseError, match="Unknown option"):
            CommandArgumentParser.parse_args('--invalid test', schema)

    def test_parse_missing_value_raises(self):
        """Test that missing value for option raises error."""
        schema = {'name': {'type': str, 'default': 'default'}}
        with pytest.raises(ArgumentParseError, match="requires a value"):
            CommandArgumentParser.parse_args('--name', schema)

    def test_parse_invalid_type_raises(self):
        """Test that invalid type conversion raises error."""
        schema = {'count': {'type': int, 'default': 1}}
        with pytest.raises(ArgumentParseError, match="expected int"):
            CommandArgumentParser.parse_args('--count abc', schema)

    def test_parse_empty_string(self):
        """Test parsing empty string returns defaults."""
        schema = {
            'name': {'type': str, 'default': 'default'},
            'count': {'type': int, 'default': 1}
        }
        result = CommandArgumentParser.parse_args('', schema)
        assert result['name'] == 'default'
        assert result['count'] == 1

    def test_parse_hyphen_to_underscore(self):
        """Test that hyphens in options convert to underscores."""
        schema = {'home_team': {'type': int, 'default': 1}}
        result = CommandArgumentParser.parse_args('--home-team 5', schema)
        assert result['home_team'] == 5

    def test_parse_float_arg(self):
        """Test parsing float argument."""
        schema = {'ratio': {'type': float, 'default': 1.0}}
        result = CommandArgumentParser.parse_args('--ratio 3.14', schema)
        assert result['ratio'] == 3.14

    def test_parse_string_list(self):
        """Test parsing comma-separated string list."""
        schema = {'tags': {'type': list, 'default': []}}
        result = CommandArgumentParser.parse_args('--tags one,two,three', schema)
        assert result['tags'] == ['one', 'two', 'three']

    def test_parse_multiple_flags(self):
        """Test parsing multiple boolean flags."""
        schema = {
            'verbose': {'type': bool, 'flag': True, 'default': False},
            'debug': {'type': bool, 'flag': True, 'default': False}
        }
        result = CommandArgumentParser.parse_args('--verbose --debug', schema)
        assert result['verbose'] is True
        assert result['debug'] is True

    def test_parse_unexpected_positional_raises(self):
        """Test that unexpected positional argument raises error."""
        schema = {'name': {'type': str, 'default': 'default'}}
        with pytest.raises(ArgumentParseError, match="Unexpected positional"):
            CommandArgumentParser.parse_args('extra_arg', schema)

    def test_parse_invalid_int_list_raises(self):
        """Test that invalid integer in list raises error."""
        schema = {'bases': {'type': 'int_list', 'default': []}}
        with pytest.raises(ArgumentParseError, match="expected int_list"):
            CommandArgumentParser.parse_args('--bases 1,abc,3', schema)

    def test_parse_invalid_syntax_raises(self):
        """Test that invalid shell syntax raises error."""
        schema = {'name': {'type': str, 'default': 'default'}}
        with pytest.raises(ArgumentParseError, match="Invalid argument syntax"):
            CommandArgumentParser.parse_args('--name "unclosed quote', schema)

    def test_parse_game_id_with_option(self):
        """Test parsing game ID from option."""
        arg_string = '--game-id a1b2c3d4-e5f6-7890-abcd-ef1234567890'
        result = CommandArgumentParser.parse_game_id(arg_string)
        assert result == 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'

    def test_parse_game_id_positional(self):
        """Test parsing game ID as positional argument."""
        arg_string = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
        result = CommandArgumentParser.parse_game_id(arg_string)
        assert result == 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'

    def test_parse_game_id_none(self):
        """Test parsing game ID returns None when not found."""
        arg_string = '--other-option value'
        result = CommandArgumentParser.parse_game_id(arg_string)
        assert result is None

    def test_parse_game_id_invalid_syntax(self):
        """Test parsing game ID with invalid syntax returns None."""
        arg_string = '"unclosed quote'
        result = CommandArgumentParser.parse_game_id(arg_string)
        assert result is None


class TestPrebuiltParsers:
    """Tests for pre-built parser functions."""

    def test_parse_new_game_args_defaults(self):
        """Test new_game parser with defaults."""
        result = parse_new_game_args('')
        assert result['league'] == 'sba'
        assert result['home_team'] == 1
        assert result['away_team'] == 2

    def test_parse_new_game_args_custom(self):
        """Test new_game parser with custom values."""
        result = parse_new_game_args('--league pd --home-team 5 --away-team 3')
        assert result['league'] == 'pd'
        assert result['home_team'] == 5
        assert result['away_team'] == 3

    def test_parse_defensive_args_defaults(self):
        """Test defensive parser with defaults."""
        result = parse_defensive_args('')
        assert result['alignment'] == 'normal'
        assert result['infield'] == 'normal'
        assert result['outfield'] == 'normal'
        assert result['hold'] == []

    def test_parse_defensive_args_with_hold(self):
        """Test defensive parser with hold runners."""
        result = parse_defensive_args('--alignment shifted_left --hold 1,3')
        assert result['alignment'] == 'shifted_left'
        assert result['hold'] == [1, 3]

    def test_parse_defensive_args_all_options(self):
        """Test defensive parser with all options."""
        result = parse_defensive_args('--alignment extreme_shift --infield back --outfield in --hold 1,2,3')
        assert result['alignment'] == 'extreme_shift'
        assert result['infield'] == 'back'
        assert result['outfield'] == 'in'
        assert result['hold'] == [1, 2, 3]

    def test_parse_offensive_args_defaults(self):
        """Test offensive parser with defaults."""
        result = parse_offensive_args('')
        assert result['action'] == 'swing_away'
        assert result['steal'] == []

    def test_parse_offensive_args_action(self):
        """Test offensive parser with action."""
        result = parse_offensive_args('--action hit_and_run')
        assert result['action'] == 'hit_and_run'

    def test_parse_offensive_args_steal(self):
        """Test offensive parser with steal attempts."""
        result = parse_offensive_args('--steal 2,3')
        assert result['steal'] == [2, 3]

    def test_parse_offensive_args_all_options(self):
        """Test offensive parser with all options."""
        result = parse_offensive_args('--action steal --steal 2')
        assert result['action'] == 'steal'
        assert result['steal'] == [2]

    def test_parse_quick_play_args_default(self):
        """Test quick_play parser with default."""
        result = parse_quick_play_args('')
        assert result['count'] == 1

    def test_parse_quick_play_args_positional(self):
        """Test quick_play parser with positional count."""
        result = parse_quick_play_args('10')
        assert result['count'] == 10

    def test_parse_quick_play_args_large_count(self):
        """Test quick_play parser with large count."""
        result = parse_quick_play_args('100')
        assert result['count'] == 100

    def test_parse_use_game_args_valid(self):
        """Test use_game parser with valid UUID."""
        result = parse_use_game_args('a1b2c3d4-e5f6-7890-abcd-ef1234567890')
        assert result['game_id'] == 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'

    def test_parse_use_game_args_missing_returns_empty(self):
        """Test use_game parser returns empty dict when game_id missing."""
        result = parse_use_game_args('')
        # game_id is positional without default, so it won't be in result
        assert 'game_id' not in result or result.get('game_id') is None
