"""
Terminal client main entry point - Click CLI application.

Direct GameEngine testing without WebSockets.

Usage:
    python -m terminal_client start-game --league sba
    python -m terminal_client defensive --alignment normal
    python -m terminal_client offensive --approach power
    python -m terminal_client resolve
    python -m terminal_client status

Author: Claude
Date: 2025-10-26
"""
import logging
import asyncio
from uuid import UUID, uuid4
from typing import Optional
import click

from app.core.game_engine import game_engine
from app.core.state_manager import state_manager
from app.models.game_models import DefensiveDecision, OffensiveDecision
from app.database.operations import DatabaseOperations
from terminal_client import display
from terminal_client.config import Config
from terminal_client.commands import game_commands

# Setup logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(f'{__name__}.main')


def set_current_game(game_id: UUID) -> None:
    """Set the current game ID in persistent config."""
    Config.set_current_game(game_id)
    display.print_info(f"Current game set to: {game_id}")


def get_current_game() -> UUID:
    """Get the current game ID from persistent config or raise error."""
    game_id = Config.get_current_game()
    if game_id is None:
        display.print_error("No current game set. Use 'new-game' or 'use-game' first.")
        raise click.Abort()
    return game_id


@click.group()
def cli():
    """Terminal UI for testing game engine directly."""
    pass


@cli.command('start-game')
@click.option('--league', default='sba', help='League (sba or pd)')
@click.option('--game-id', default=None, help='Game UUID (auto-generated if not provided)')
@click.option('--home-team', default=1, help='Home team ID')
@click.option('--away-team', default=2, help='Away team ID')
def start_game(league, game_id, home_team, away_team):
    """Start a new game and transition to active."""
    async def _start():
        # Generate or parse game ID
        gid = UUID(game_id) if game_id else uuid4()

        # Create game in state manager
        state = await state_manager.create_game(
            game_id=gid,
            league_id=league,
            home_team_id=home_team,
            away_team_id=away_team
        )

        display.print_success(f"Game created: {gid}")
        display.print_info(f"League: {league}, Home: {home_team}, Away: {away_team}")

        # Set as current game
        set_current_game(gid)

        # Start the game
        try:
            state = await game_engine.start_game(gid)
            display.print_success(f"Game started - Inning {state.inning} {state.half}")
            display.display_game_state(state)
        except Exception as e:
            display.print_error(f"Failed to start game: {e}")
            raise click.Abort()

    asyncio.run(_start())


@cli.command()
@click.option('--game-id', default=None, help='Game UUID (uses current if not provided)')
@click.option('--alignment', default='normal', help='Defensive alignment')
@click.option('--infield', default='normal', help='Infield depth')
@click.option('--outfield', default='normal', help='Outfield depth')
@click.option('--hold', default=None, help='Comma-separated bases to hold (e.g., 1,3)')
def defensive(game_id, alignment, infield, outfield, hold):
    """
    Submit defensive decision.

    Valid alignment: normal, shifted_left, shifted_right, extreme_shift
    Valid infield: in, normal, back, double_play
    Valid outfield: in, normal, back
    """
    async def _defensive():
        gid = UUID(game_id) if game_id else get_current_game()

        # Parse hold runners
        hold_list = []
        if hold:
            try:
                hold_list = [int(b.strip()) for b in hold.split(',')]
            except ValueError:
                display.print_error("Invalid hold format. Use comma-separated numbers (e.g., '1,3')")
                raise click.Abort()

        # Use shared command
        success = await game_commands.submit_defensive_decision(
            game_id=gid,
            alignment=alignment,
            infield=infield,
            outfield=outfield,
            hold_runners=hold_list
        )

        if not success:
            raise click.Abort()

    asyncio.run(_defensive())


@cli.command()
@click.option('--game-id', default=None, help='Game UUID (uses current if not provided)')
@click.option('--action', default='swing_away', help='Offensive action (swing_away, steal, check_jump, hit_and_run, sac_bunt, squeeze_bunt)')
@click.option('--steal', default=None, help='Comma-separated bases to steal (e.g., 2,3)')
def offensive(game_id, action, steal):
    """
    Submit offensive decision.

    Session 2 Update (2025-01-14): Changed --approach to --action, removed deprecated flags.
    Valid actions: swing_away (default), steal, check_jump, hit_and_run, sac_bunt, squeeze_bunt
    Valid steal: comma-separated bases (2, 3, 4) - used only when action="steal"
    """
    async def _offensive():
        gid = UUID(game_id) if game_id else get_current_game()

        # Parse steal attempts
        steal_list = []
        if steal:
            try:
                steal_list = [int(b.strip()) for b in steal.split(',')]
            except ValueError:
                display.print_error("Invalid steal format. Use comma-separated numbers (e.g., '2,3')")
                raise click.Abort()

        # Use shared command
        success = await game_commands.submit_offensive_decision(
            game_id=gid,
            action=action,
            steal_attempts=steal_list
        )

        if not success:
            raise click.Abort()

    asyncio.run(_offensive())


@cli.command()
@click.option('--game-id', default=None, help='Game UUID (uses current if not provided)')
def resolve(game_id):
    """Resolve the current play (both decisions must be submitted first)."""
    async def _resolve():
        gid = UUID(game_id) if game_id else get_current_game()

        # Use shared command
        success = await game_commands.resolve_play(gid)

        if not success:
            raise click.Abort()

    asyncio.run(_resolve())


@cli.command()
@click.option('--game-id', default=None, help='Game UUID (uses current if not provided)')
def status(game_id):
    """Display current game state."""
    async def _status():
        gid = UUID(game_id) if game_id else get_current_game()

        # Use shared command
        success = await game_commands.show_game_status(gid)

        if not success:
            raise click.Abort()

    asyncio.run(_status())


@cli.command('box-score')
@click.option('--game-id', default=None, help='Game UUID (uses current if not provided)')
def box_score(game_id):
    """Display box score (simple version)."""
    async def _box_score():
        gid = UUID(game_id) if game_id else get_current_game()

        # Use shared command
        success = await game_commands.show_box_score(gid)

        if not success:
            raise click.Abort()

    asyncio.run(_box_score())


@cli.command('list-games')
def list_games():
    """List all games in state manager."""
    games = state_manager.list_games()

    if not games:
        display.print_warning("No active games in state manager")
        return

    display.print_info(f"Active games: {len(games)}")
    for game_id in games:
        display.console.print(f"  • {game_id}")


@cli.command('use-game')
@click.argument('game_id')
def use_game(game_id):
    """Set current game ID."""
    try:
        gid = UUID(game_id)
        set_current_game(gid)
    except ValueError:
        display.print_error(f"Invalid UUID: {game_id}")
        raise click.Abort()


@cli.command('quick-play')
@click.option('--count', default=1, help='Number of plays to execute')
@click.option('--game-id', default=None, help='Game UUID (uses current if not provided)')
def quick_play(count, game_id):
    """
    Quick play mode - submit default decisions and resolve multiple plays.

    Useful for rapidly advancing the game for testing.
    """
    async def _quick_play():
        gid = UUID(game_id) if game_id else get_current_game()

        # Use shared command
        await game_commands.quick_play_rounds(gid, count)

    asyncio.run(_quick_play())


@cli.command('new-game')
@click.option('--league', default='sba', help='League (sba or pd)')
@click.option('--home-team', default=1, help='Home team ID')
@click.option('--away-team', default=2, help='Away team ID')
def new_game(league, home_team, away_team):
    """
    Create a new game with lineups and start it immediately (all-in-one).

    This is a convenience command that combines:
    1. Creating game state
    2. Setting up test lineups
    3. Starting the game

    Perfect for rapid testing!
    """
    async def _new_game():
        # Use shared command
        await game_commands.create_new_game(
            league=league,
            home_team=home_team,
            away_team=away_team,
            set_current=True
        )

    asyncio.run(_new_game())


@cli.command('setup-game')
@click.option('--game-id', default=None, help='Game UUID (uses current if not provided)')
@click.option('--league', default='sba', help='League (sba or pd)')
def setup_game(game_id, league):
    """
    Create test lineups for both teams to allow game to start.

    Generates 9 players per team with proper positions and batting order.
    Uses mock player/card IDs for testing.
    """
    async def _setup():
        gid = UUID(game_id) if game_id else get_current_game()

        db_ops = DatabaseOperations()

        # Standard defensive positions
        positions = ['P', 'C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF']

        try:
            # Get game to determine team IDs
            state = await game_engine.get_game_state(gid)
            if not state:
                display.print_error(f"Game {gid} not found")
                raise click.Abort()

            display.print_info(f"Setting up lineups for game {gid}")
            display.print_info(f"League: {league}, Home Team: {state.home_team_id}, Away Team: {state.away_team_id}")

            # Create lineups for both teams
            for team_id in [state.home_team_id, state.away_team_id]:
                team_name = "Home" if team_id == state.home_team_id else "Away"
                display.print_info(f"Creating {team_name} team lineup (Team ID: {team_id})...")

                for i, position in enumerate(positions, start=1):
                    batting_order = i

                    if league == 'sba':
                        # SBA uses player_id
                        player_id = (team_id * 100) + i  # Generate unique player IDs
                        await db_ops.add_sba_lineup_player(
                            game_id=gid,
                            team_id=team_id,
                            player_id=player_id,
                            position=position,
                            batting_order=batting_order,
                            is_starter=True
                        )
                        display.console.print(f"  ✓ Added {position} (Player #{player_id}) - Batting {batting_order}")
                    else:
                        # PD uses card_id
                        card_id = (team_id * 100) + i  # Generate unique card IDs
                        await db_ops.add_pd_lineup_card(
                            game_id=gid,
                            team_id=team_id,
                            card_id=card_id,
                            position=position,
                            batting_order=batting_order,
                            is_starter=True
                        )
                        display.console.print(f"  ✓ Added {position} (Card #{card_id}) - Batting {batting_order}")

            display.print_success(f"Lineups created successfully!")
            display.print_info("You can now start the game with: python -m terminal_client start-game --game-id <uuid>")
            display.print_info("Or if this is your current game, just run: python -m terminal_client start-game")

        except Exception as e:
            display.print_error(f"Failed to setup game: {e}")
            logger.exception("Setup error")
            raise click.Abort()

    asyncio.run(_setup())


@cli.command('config')
@click.option('--clear', is_flag=True, help='Clear current game from config')
def config_cmd(clear):
    """
    Show or manage terminal client configuration.

    Displays config file location and current game.
    Use --clear to reset the current game.
    """
    config_path = Config.get_config_path()
    display.print_info(f"Config file: {config_path}")

    if clear:
        Config.clear_current_game()
        display.print_success("Current game cleared")
        return

    current_game = Config.get_current_game()
    if current_game:
        display.console.print(f"\n[green]Current game:[/green] {current_game}")
    else:
        display.console.print("\n[yellow]No current game set[/yellow]")


@cli.command('help-cmd')
@click.argument('command', required=False)
def show_cli_help(command):
    """
    Show help for terminal client commands.

    Usage:
      python -m terminal_client help-cmd              # Show all commands
      python -m terminal_client help-cmd new-game     # Show help for specific command
    """
    from terminal_client.help_text import show_help

    # Convert hyphenated command to underscore for lookup
    if command:
        command = command.replace('-', '_')

    show_help(command)


if __name__ == "__main__":
    cli()
