"""
Tab completion support for terminal client REPL.

Provides intelligent completion for commands, options, and values
using Python's cmd module completion hooks.

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

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


class CompletionHelper:
    """Helper class for generating tab completions."""

    # Valid values for common options
    VALID_LEAGUES = ['sba', 'pd']
    VALID_ALIGNMENTS = ['normal', 'shifted_left', 'shifted_right', 'extreme_shift']
    VALID_INFIELD_DEPTHS = ['in', 'normal', 'back', 'double_play']
    VALID_OUTFIELD_DEPTHS = ['in', 'normal', 'back']
    VALID_APPROACHES = ['normal', 'contact', 'power', 'patient']

    # Valid bases for stealing/holding
    VALID_BASES = ['1', '2', '3']

    # Valid PlayOutcome values for resolve_with command
    VALID_OUTCOMES = [
        'strikeout',
        'groundball_a', 'groundball_b', 'groundball_c',
        'flyout_a', 'flyout_b', 'flyout_c',
        'lineout', 'popout',
        'single_1', 'single_2', 'single_uncapped',
        'double_2', 'double_3', 'double_uncapped',
        'triple', 'homerun',
        'walk', 'hbp', 'intentional_walk',
        'error',
        'wild_pitch', 'passed_ball', 'stolen_base', 'caught_stealing', 'balk', 'pick_off',
        'bp_homerun', 'bp_single', 'bp_flyout', 'bp_lineout'
    ]

    # Valid positions for fielding rolls
    VALID_POSITIONS = ['P', 'C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF']

    @staticmethod
    def filter_completions(text: str, options: List[str]) -> List[str]:
        """
        Filter options that start with the given text.

        Args:
            text: Partial text to match
            options: List of possible completions

        Returns:
            List of matching options
        """
        if not text:
            return options
        return [opt for opt in options if opt.startswith(text)]

    @staticmethod
    def complete_option(text: str, line: str, available_options: List[str]) -> List[str]:
        """
        Complete option names (--option).

        Args:
            text: Current text being completed
            line: Full command line
            available_options: List of valid option names

        Returns:
            List of matching options with -- prefix
        """
        if text.startswith('--'):
            # Completing option name
            prefix = text[2:]
            matches = [opt for opt in available_options if opt.startswith(prefix)]
            return [f'--{match}' for match in matches]
        elif not text:
            # Show all options
            return [f'--{opt}' for opt in available_options]
        return []

    @staticmethod
    def get_current_option(line: str, endidx: int) -> Optional[str]:
        """
        Determine which option we're currently completing the value for.

        Args:
            line: Full command line
            endidx: Current cursor position

        Returns:
            Option name (without --) or None
        """
        # Split line up to cursor position
        before_cursor = line[:endidx]
        tokens = before_cursor.split()

        # Look for the last --option before cursor
        for i in range(len(tokens) - 1, -1, -1):
            if tokens[i].startswith('--'):
                return tokens[i][2:].replace('-', '_')

        return None


class GameREPLCompletions:
    """Mixin class providing tab completion methods for GameREPL."""

    def __init__(self):
        """Initialize completion helper."""
        self.completion_helper = CompletionHelper()

    # ==================== Command Completions ====================

    def complete_new_game(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
        """
        Complete new_game command.

        Available options:
          --league sba|pd
          --home-team N
          --away-team N
        """
        available_options = ['league', 'home-team', 'away-team']

        # Check if we're completing an option value first
        current_option = self.completion_helper.get_current_option(line, endidx)

        if current_option == 'league':
            return self.completion_helper.filter_completions(
                text, self.completion_helper.VALID_LEAGUES
            )

        # Check if we're completing an option name
        if text.startswith('--'):
            return self.completion_helper.complete_option(text, line, available_options)
        elif not text:
            return self.completion_helper.complete_option(text, line, available_options)

        return []

    def complete_defensive(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
        """
        Complete defensive command.

        Available options:
          --alignment normal|shifted_left|shifted_right|extreme_shift
          --infield in|normal|back|double_play
          --outfield in|normal|back
          --hold 1,2,3
        """
        available_options = ['alignment', 'infield', 'outfield', 'hold']

        # Check if we're completing an option value first
        current_option = self.completion_helper.get_current_option(line, endidx)

        if current_option == 'alignment':
            return self.completion_helper.filter_completions(
                text, self.completion_helper.VALID_ALIGNMENTS
            )
        elif current_option == 'infield':
            return self.completion_helper.filter_completions(
                text, self.completion_helper.VALID_INFIELD_DEPTHS
            )
        elif current_option == 'outfield':
            return self.completion_helper.filter_completions(
                text, self.completion_helper.VALID_OUTFIELD_DEPTHS
            )
        elif current_option == 'hold':
            # For comma-separated values, complete the last item
            if ',' in text:
                prefix = text.rsplit(',', 1)[0] + ','
                last_item = text.rsplit(',', 1)[1]
                matches = self.completion_helper.filter_completions(
                    last_item, self.completion_helper.VALID_BASES
                )
                return [f'{prefix}{match}' for match in matches]
            else:
                return self.completion_helper.filter_completions(
                    text, self.completion_helper.VALID_BASES
                )

        # Check if we're completing an option name
        if text.startswith('--'):
            return self.completion_helper.complete_option(text, line, available_options)
        elif not text:
            return self.completion_helper.complete_option(text, line, available_options)

        return []

    def complete_offensive(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
        """
        Complete offensive command.

        Available options:
          --approach normal|contact|power|patient
          --steal 2,3
          --hit-run (flag)
          --bunt (flag)
        """
        available_options = ['approach', 'steal', 'hit-run', 'bunt']

        # Check if we're completing an option value first
        current_option = self.completion_helper.get_current_option(line, endidx)

        if current_option == 'approach':
            return self.completion_helper.filter_completions(
                text, self.completion_helper.VALID_APPROACHES
            )
        elif current_option == 'steal':
            # Only bases 2 and 3 can be stolen
            valid_steal_bases = ['2', '3']
            if ',' in text:
                prefix = text.rsplit(',', 1)[0] + ','
                last_item = text.rsplit(',', 1)[1]
                matches = self.completion_helper.filter_completions(
                    last_item, valid_steal_bases
                )
                return [f'{prefix}{match}' for match in matches]
            else:
                return self.completion_helper.filter_completions(
                    text, valid_steal_bases
                )

        # Check if we're completing an option name
        if text.startswith('--'):
            return self.completion_helper.complete_option(text, line, available_options)
        elif not text:
            return self.completion_helper.complete_option(text, line, available_options)

        return []

    def complete_use_game(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
        """
        Complete use_game command with available game IDs.
        """
        # Import here to avoid circular dependency
        from app.core.state_manager import state_manager

        # Get list of active games
        game_ids = state_manager.list_games()
        game_id_strs = [str(gid) for gid in game_ids]

        return self.completion_helper.filter_completions(text, game_id_strs)

    def complete_quick_play(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
        """
        Complete quick_play command with common counts.
        """
        # Suggest common play counts
        common_counts = ['1', '5', '10', '27', '50', '100']

        # Check if completing a positional number
        if text and text.isdigit():
            return self.completion_helper.filter_completions(text, common_counts)
        elif not text and line.strip() == 'quick_play':
            return common_counts

        return []

    # ==================== New Command Completions ====================

    def complete_roll_jump(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
        """
        Complete roll_jump command.

        Usage: roll_jump [league]
        """
        return self.completion_helper.filter_completions(
            text, self.completion_helper.VALID_LEAGUES
        )

    def complete_test_jump(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
        """
        Complete test_jump command.

        Usage: test_jump [count] [league]
        """
        parts = line.split()
        num_args = len(parts) - 1  # Exclude command name

        if num_args == 0 or (num_args == 1 and not text):
            # Suggest common counts
            common_counts = ['10', '50', '100', '500', '1000']
            return self.completion_helper.filter_completions(text, common_counts)
        elif num_args == 1 or (num_args == 2 and not text):
            # Suggest leagues
            return self.completion_helper.filter_completions(
                text, self.completion_helper.VALID_LEAGUES
            )

        return []

    def complete_roll_fielding(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
        """
        Complete roll_fielding command.

        Usage: roll_fielding <position> [league]
        """
        parts = line.split()
        num_args = len(parts) - 1  # Exclude command name

        if num_args == 0 or (num_args == 1 and not text):
            # Suggest positions
            return self.completion_helper.filter_completions(
                text, self.completion_helper.VALID_POSITIONS
            )
        elif num_args == 1 or (num_args == 2 and not text):
            # Suggest leagues
            return self.completion_helper.filter_completions(
                text, self.completion_helper.VALID_LEAGUES
            )

        return []

    def complete_test_fielding(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
        """
        Complete test_fielding command.

        Usage: test_fielding <position> [count] [league]
        """
        parts = line.split()
        num_args = len(parts) - 1  # Exclude command name

        if num_args == 0 or (num_args == 1 and not text):
            # Suggest positions
            return self.completion_helper.filter_completions(
                text, self.completion_helper.VALID_POSITIONS
            )
        elif num_args == 1 or (num_args == 2 and not text):
            # Suggest common counts
            common_counts = ['10', '50', '100', '500', '1000']
            return self.completion_helper.filter_completions(text, common_counts)
        elif num_args == 2 or (num_args == 3 and not text):
            # Suggest leagues
            return self.completion_helper.filter_completions(
                text, self.completion_helper.VALID_LEAGUES
            )

        return []

    # ==================== Helper Methods ====================

    def completedefault(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
        """
        Default completion handler for commands without specific completers.

        Provides basic option completion if line contains options.
        """
        # If text starts with --, try to show common options
        if text.startswith('--'):
            common_options = ['help', 'verbose', 'debug']
            return self.completion_helper.complete_option(text, line, common_options)

        return []

    def complete_resolve_with(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
        """
        Complete resolve_with command with PlayOutcome values.

        Usage: resolve_with <outcome>

        Provides completions for all valid PlayOutcome enum values.
        """
        # Complete the outcome argument (first and only argument)
        return self.completion_helper.filter_completions(
            text, self.completion_helper.VALID_OUTCOMES
        )

    def completenames(self, text: str, *ignored) -> List[str]:
        """
        Override completenames to provide better command completion.

        This is called when completing the first word (command name).
        """
        # Get all do_* methods
        dotext = 'do_' + text
        commands = [name[3:] for name in self.get_names() if name.startswith(dotext)]

        # Add aliases
        if 'exit'.startswith(text):
            commands.append('exit')
        if 'quit'.startswith(text):
            commands.append('quit')

        return commands


# Example completion mappings for reference
COMPLETION_EXAMPLES = """
# Example Tab Completion Usage:

⚾ > new_game --<TAB>
--league  --home-team  --away-team

⚾ > new_game --league <TAB>
sba  pd

⚾ > defensive --<TAB>
--alignment  --infield  --outfield  --hold

⚾ > defensive --alignment <TAB>
normal  shifted_left  shifted_right  extreme_shift

⚾ > defensive --hold 1,<TAB>
1,2  1,3

⚾ > offensive --approach <TAB>
normal  contact  power  patient

⚾ > use_game <TAB>
[shows all active game UUIDs]

⚾ > quick_play <TAB>
1  5  10  27  50  100
"""
