"""
Test script to simulate a complete at-bat

Tests the full GameEngine flow:
1. Create game in state manager
2. Start game
3. Submit defensive decision
4. Submit offensive decision
5. Resolve play
6. Check results

Author: Claude
Date: 2025-10-24
"""
import asyncio
import sys
from pathlib import Path

# Add backend to path
sys.path.insert(0, str(Path(__file__).parent.parent))

from uuid import uuid4
from app.core.state_manager import state_manager
from app.core.game_engine import game_engine
from app.database.operations import DatabaseOperations
from app.models.game_models import DefensiveDecision, OffensiveDecision


async def test_single_at_bat():
    """Test a single at-bat flow"""
    print("=" * 60)
    print("TESTING SINGLE AT-BAT FLOW")
    print("=" * 60)

    # Create game
    game_id = uuid4()
    print(f"\n1. Creating game {game_id}...")

    # Create in database first
    db_ops = DatabaseOperations()
    await db_ops.create_game(
        game_id=game_id,
        league_id="sba",
        home_team_id=1,
        away_team_id=2,
        game_mode="friendly",
        visibility="public"
    )

    # Create dummy lineups for testing
    # Away team lineup
    positions = ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF"]
    for i in range(1, 10):
        await db_ops.add_sba_lineup_player(
            game_id=game_id,
            team_id=2,  # Away team
            player_id=100 + i,  # Dummy player IDs
            position=positions[i-1],
            batting_order=i
        )

    # Home team lineup
    for i in range(1, 10):
        await db_ops.add_sba_lineup_player(
            game_id=game_id,
            team_id=1,  # Home team
            player_id=200 + i,  # Dummy player IDs
            position=positions[i-1],
            batting_order=i
        )

    # Then create in state manager
    state = await state_manager.create_game(
        game_id=game_id,
        league_id="sba",
        home_team_id=1,
        away_team_id=2
    )
    print(f"   ✅ Game created with lineups - Status: {state.status}")

    # Start game
    print("\n2. Starting game...")
    state = await game_engine.start_game(game_id)
    print(f"   ✅ Game started")
    print(f"      Inning: {state.inning} {state.half}")
    print(f"      Outs: {state.outs}")
    print(f"      Status: {state.status}")

    # Defensive decision
    print("\n3. Submitting defensive decision...")
    def_decision = DefensiveDecision(
        alignment="normal",
        infield_depth="normal",
        outfield_depth="normal"
    )
    state = await game_engine.submit_defensive_decision(game_id, def_decision)
    print(f"   ✅ Defensive decision submitted")
    print(f"      Pending: {state.pending_decision}")

    # Offensive decision
    print("\n4. Submitting offensive decision...")
    off_decision = OffensiveDecision(
        approach="normal"
    )
    state = await game_engine.submit_offensive_decision(game_id, off_decision)
    print(f"   ✅ Offensive decision submitted")
    print(f"      Pending: {state.pending_decision}")

    # Resolve play
    print("\n5. Resolving play...")
    result = await game_engine.resolve_play(game_id)
    print(f"   ✅ Play resolved!")
    print(f"      Outcome: {result.outcome}")
    print(f"      Description: {result.description}")
    print(f"      AB Roll: {result.ab_roll}")
    print(f"      Outs Recorded: {result.outs_recorded}")
    print(f"      Runs Scored: {result.runs_scored}")
    print(f"      Is Hit: {result.is_hit}")
    print(f"      Is Out: {result.is_out}")

    # Check final state
    print("\n6. Checking final state...")
    final_state = await game_engine.get_game_state(game_id)
    print(f"   📊 Game State:")
    print(f"      Inning: {final_state.inning} {final_state.half}")
    print(f"      Outs: {final_state.outs}")
    print(f"      Score: Away {final_state.away_score} - Home {final_state.home_score}")
    print(f"      Runners: {len(final_state.runners)}")
    if final_state.runners:
        for runner in final_state.runners:
            print(f"         - Runner on {runner.on_base}B")
    print(f"      Play Count: {final_state.play_count}")
    print(f"      Last Result: {final_state.last_play_result}")

    print("\n" + "=" * 60)
    print("✅ SINGLE AT-BAT TEST PASSED")
    print("=" * 60)

    return game_id, final_state


async def test_full_inning():
    """Test a complete half-inning (3 outs)"""
    print("\n\n")
    print("=" * 60)
    print("TESTING FULL HALF-INNING (3 OUTS)")
    print("=" * 60)

    # Create and start game
    game_id = uuid4()
    print(f"\n1. Creating and starting game {game_id}...")

    # Create in database first
    db_ops = DatabaseOperations()
    await db_ops.create_game(
        game_id=game_id,
        league_id="sba",
        home_team_id=1,
        away_team_id=2,
        game_mode="friendly",
        visibility="public"
    )

    # Create dummy lineups for testing
    positions = ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF"]
    # Away team lineup
    for i in range(1, 10):
        await db_ops.add_sba_lineup_player(
            game_id=game_id,
            team_id=2,  # Away team
            player_id=100 + i,
            position=positions[i-1],
            batting_order=i
        )

    # Home team lineup
    for i in range(1, 10):
        await db_ops.add_sba_lineup_player(
            game_id=game_id,
            team_id=1,  # Home team
            player_id=200 + i,
            position=positions[i-1],
            batting_order=i
        )

    # Then create in state manager
    state = await state_manager.create_game(
        game_id=game_id,
        league_id="sba",
        home_team_id=1,
        away_team_id=2
    )
    await game_engine.start_game(game_id)
    print(f"   ✅ Game started with lineups")

    at_bat_num = 1

    # Keep playing until 3 outs
    while True:
        state = await game_engine.get_game_state(game_id)

        if state.outs >= 3:
            print(f"\n   🎯 3 outs reached - inning over!")
            break

        print(f"\n{at_bat_num}. At-Bat #{at_bat_num} (Inning {state.inning} {state.half}, {state.outs} outs)...")

        # Submit decisions
        def_decision = DefensiveDecision(alignment="normal")
        await game_engine.submit_defensive_decision(game_id, def_decision)

        off_decision = OffensiveDecision(approach="normal")
        await game_engine.submit_offensive_decision(game_id, off_decision)

        # Resolve
        result = await game_engine.resolve_play(game_id)
        print(f"   Result: {result.description}")
        print(f"   Roll: {result.ab_roll.check_d20} (d20)")
        print(f"   Outs: {result.outs_recorded}, Runs: {result.runs_scored}")

        at_bat_num += 1

        # Safety check
        if at_bat_num > 50:
            print("   ⚠️  Safety limit reached - stopping test")
            break

    # Check final state
    final_state = await game_engine.get_game_state(game_id)
    print(f"\n📊 Final State:")
    print(f"   Inning: {final_state.inning} {final_state.half}")
    print(f"   Total At-Bats: {at_bat_num - 1}")
    print(f"   Score: Away {final_state.away_score} - Home {final_state.home_score}")
    print(f"   Total Plays: {final_state.play_count}")

    print("\n" + "=" * 60)
    print("✅ FULL HALF-INNING TEST PASSED")
    print("=" * 60)


async def test_lineup_validation():
    """Test that start_game() fails with incomplete lineups"""
    print("\n\n")
    print("=" * 60)
    print("TESTING LINEUP VALIDATION")
    print("=" * 60)

    # Test 1: Start game with no lineups
    game_id = uuid4()
    print(f"\n1. Testing start_game() with NO lineups...")

    db_ops = DatabaseOperations()
    await db_ops.create_game(
        game_id=game_id,
        league_id="sba",
        home_team_id=1,
        away_team_id=2,
        game_mode="friendly",
        visibility="public"
    )

    state = await state_manager.create_game(
        game_id=game_id,
        league_id="sba",
        home_team_id=1,
        away_team_id=2
    )

    try:
        await game_engine.start_game(game_id)
        print("   ❌ FAIL: Should have raised ValidationError")
        sys.exit(1)
    except Exception as e:
        print(f"   ✅ Correctly rejected: {e}")

    # Test 2: Start game with incomplete lineup (missing positions)
    game_id = uuid4()
    print(f"\n2. Testing start_game() with INCOMPLETE lineups (only 5 players)...")

    await db_ops.create_game(
        game_id=game_id,
        league_id="sba",
        home_team_id=1,
        away_team_id=2,
        game_mode="friendly",
        visibility="public"
    )

    # Add only 5 players per team
    for i in range(1, 6):
        await db_ops.add_sba_lineup_player(
            game_id=game_id,
            team_id=1,
            player_id=200 + i,
            position=["P", "C", "1B", "2B", "3B"][i-1],
            batting_order=i
        )
        await db_ops.add_sba_lineup_player(
            game_id=game_id,
            team_id=2,
            player_id=100 + i,
            position=["P", "C", "1B", "2B", "3B"][i-1],
            batting_order=i
        )

    state = await state_manager.create_game(
        game_id=game_id,
        league_id="sba",
        home_team_id=1,
        away_team_id=2
    )

    try:
        await game_engine.start_game(game_id)
        print("   ❌ FAIL: Should have raised ValidationError")
        sys.exit(1)
    except Exception as e:
        print(f"   ✅ Correctly rejected: {e}")

    print("\n" + "=" * 60)
    print("✅ LINEUP VALIDATION TEST PASSED")
    print("=" * 60)


async def test_snapshot_tracking():
    """Test on_base_code and runner tracking in Play records"""
    print("\n\n")
    print("=" * 60)
    print("TESTING SNAPSHOT TRACKING")
    print("=" * 60)

    # Create game with lineups
    game_id = uuid4()
    db_ops = DatabaseOperations()
    await db_ops.create_game(
        game_id=game_id,
        league_id="sba",
        home_team_id=1,
        away_team_id=2,
        game_mode="friendly",
        visibility="public"
    )

    positions = ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF"]
    for i in range(1, 10):
        await db_ops.add_sba_lineup_player(
            game_id=game_id,
            team_id=2,
            player_id=100 + i,
            position=positions[i-1],
            batting_order=i
        )
        await db_ops.add_sba_lineup_player(
            game_id=game_id,
            team_id=1,
            player_id=200 + i,
            position=positions[i-1],
            batting_order=i
        )

    state = await state_manager.create_game(
        game_id=game_id,
        league_id="sba",
        home_team_id=1,
        away_team_id=2
    )
    await game_engine.start_game(game_id)

    print(f"\n1. Playing until we get runners on base...")

    # Play until we have runners
    max_attempts = 20
    for attempt in range(max_attempts):
        state = await game_engine.get_game_state(game_id)

        await game_engine.submit_defensive_decision(game_id, DefensiveDecision())
        await game_engine.submit_offensive_decision(game_id, OffensiveDecision())
        result = await game_engine.resolve_play(game_id)

        if state.runners:
            print(f"   ✅ Got {len(state.runners)} runner(s) on base after {attempt+1} plays")
            break

        if state.outs >= 3:
            # Reset for new half inning
            continue

    # Verify snapshot tracking
    print(f"\n2. Checking snapshot fields in GameState...")
    state = await game_engine.get_game_state(game_id)
    print(f"   Current batter lineup_id: {state.current_batter.lineup_id if state.current_batter else None}")
    print(f"   Current pitcher lineup_id: {state.current_pitcher.lineup_id if state.current_pitcher else None}")
    print(f"   Current catcher lineup_id: {state.current_catcher.lineup_id if state.current_catcher else None}")
    print(f"   Current on_base_code: {state.current_on_base_code} (binary: {bin(state.current_on_base_code)})")

    if state.current_batter and state.current_pitcher:
        print(f"   ✅ Snapshot fields properly populated")
    else:
        print(f"   ❌ FAIL: Snapshot fields not populated")
        sys.exit(1)

    # Verify on_base_code matches runners
    print(f"\n3. Verifying on_base_code matches runners...")
    expected_code = 0
    for runner in state.runners:
        if runner.on_base == 1:
            expected_code |= 1
        elif runner.on_base == 2:
            expected_code |= 2
        elif runner.on_base == 3:
            expected_code |= 4

    print(f"   Runners: {[r.on_base for r in state.runners]}")
    print(f"   Expected code: {expected_code}, Actual: {state.current_on_base_code}")

    if expected_code == state.current_on_base_code:
        print(f"   ✅ on_base_code correctly calculated")
    else:
        print(f"   ❌ FAIL: on_base_code mismatch")
        sys.exit(1)

    print("\n" + "=" * 60)
    print("✅ SNAPSHOT TRACKING TEST PASSED")
    print("=" * 60)


async def test_batting_order_cycling():
    """Test that batting order cycles 0-8 independently per team"""
    print("\n\n")
    print("=" * 60)
    print("TESTING BATTING ORDER CYCLING")
    print("=" * 60)

    # Create game with lineups
    game_id = uuid4()
    db_ops = DatabaseOperations()
    await db_ops.create_game(
        game_id=game_id,
        league_id="sba",
        home_team_id=1,
        away_team_id=2,
        game_mode="friendly",
        visibility="public"
    )

    positions = ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF"]
    for i in range(1, 10):
        await db_ops.add_sba_lineup_player(
            game_id=game_id,
            team_id=2,
            player_id=100 + i,
            position=positions[i-1],
            batting_order=i
        )
        await db_ops.add_sba_lineup_player(
            game_id=game_id,
            team_id=1,
            player_id=200 + i,
            position=positions[i-1],
            batting_order=i
        )

    state = await state_manager.create_game(
        game_id=game_id,
        league_id="sba",
        home_team_id=1,
        away_team_id=2
    )
    state = await game_engine.start_game(game_id)

    print(f"\n1. Checking initial batter indices...")
    print(f"   Away team batter idx: {state.away_team_batter_idx}")
    print(f"   Home team batter idx: {state.home_team_batter_idx}")

    # After first play, away_team should be at 1 (started at 0, advanced to 1)
    await game_engine.submit_defensive_decision(game_id, DefensiveDecision())
    await game_engine.submit_offensive_decision(game_id, OffensiveDecision())
    await game_engine.resolve_play(game_id)

    state = await game_engine.get_game_state(game_id)
    print(f"\n2. After first play...")
    print(f"   Away team batter idx: {state.away_team_batter_idx} (should be 2)")
    print(f"   Home team batter idx: {state.home_team_batter_idx} (should be 0)")
    print(f"   Explanation: Start used idx 0→1, first play used idx 1→2")

    if state.away_team_batter_idx != 2:
        print(f"   ❌ FAIL: Expected away_team_batter_idx=2")
        sys.exit(1)

    print(f"   ✅ Batting indices tracking independently")

    print("\n" + "=" * 60)
    print("✅ BATTING ORDER CYCLING TEST PASSED")
    print("=" * 60)


async def main():
    """Run all tests"""
    print("\n🎮 GAME ENGINE FLOW TESTING")
    print("Testing complete gameplay flow with GameEngine\n")

    try:
        # Original tests
        # Test 1: Single at-bat
        await test_single_at_bat()

        # Test 2: Full inning
        await test_full_inning()

        # New refactor tests
        # Test 3: Lineup validation
        await test_lineup_validation()

        # Test 4: Snapshot tracking
        await test_snapshot_tracking()

        # Test 5: Batting order cycling
        await test_batting_order_cycling()

        print("\n\n🎉 ALL TESTS PASSED!")
        print("\nGameEngine is working correctly:")
        print("  ✅ Game lifecycle management")
        print("  ✅ Decision submission")
        print("  ✅ Play resolution with AbRoll system")
        print("  ✅ State management and persistence")
        print("  ✅ Inning advancement")
        print("  ✅ Score tracking")
        print("  ✅ Lineup validation (refactor)")
        print("  ✅ Snapshot tracking with on_base_code (refactor)")
        print("  ✅ Independent batting order cycling (refactor)")

    except Exception as e:
        print(f"\n❌ TEST FAILED: {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)


if __name__ == "__main__":
    asyncio.run(main())
