import logging
from uuid import UUID, uuid4

from fastapi import APIRouter, Depends, HTTPException, Request
from pydantic import BaseModel, Field, field_validator

from app.api.dependencies import get_current_user_optional
from app.core.game_engine import game_engine
from app.core.state_manager import state_manager
from app.database.operations import DatabaseOperations
from app.services.lineup_service import lineup_service
from app.services.sba_api_client import sba_api_client

logger = logging.getLogger(f"{__name__}.games")

router = APIRouter()


class GameListItem(BaseModel):
    """Game list item model with enriched team and game state info"""

    game_id: str
    league_id: str
    status: str
    home_team_id: int
    away_team_id: int
    # Enriched fields
    home_team_name: str | None = None
    away_team_name: str | None = None
    home_team_abbrev: str | None = None
    away_team_abbrev: str | None = None
    home_score: int = 0
    away_score: int = 0
    inning: int | None = None
    half: str | None = None  # 'top' or 'bottom'
    # External schedule reference (for linking to SBA/PD schedule systems)
    schedule_game_id: int | None = None


class CreateGameRequest(BaseModel):
    """Request model for creating a new game"""

    name: str = Field(..., description="Game name")
    home_team_id: int = Field(..., description="Home team ID")
    away_team_id: int = Field(..., description="Away team ID")
    is_ai_opponent: bool = Field(default=False, description="Is AI opponent")
    season: int = Field(default=3, description="Season number")
    league_id: str = Field(default="sba", description="League ID (sba or pd)")


class CreateGameResponse(BaseModel):
    """Response model for game creation"""

    game_id: str
    message: str
    status: str


class QuickCreateRequest(BaseModel):
    """Optional request model for quick-create with custom teams"""

    home_team_id: int | None = Field(None, description="Home team ID (uses default if not provided)")
    away_team_id: int | None = Field(None, description="Away team ID (uses default if not provided)")
    schedule_game_id: int | None = Field(None, description="External schedule game ID for linking")


class LineupPlayerRequest(BaseModel):
    """Single player in lineup request"""

    player_id: int = Field(..., description="SBA player ID")
    position: str = Field(..., description="Defensive position (P, C, 1B, etc.)")
    batting_order: int | None = Field(
        None, ge=1, le=9, description="Batting order (1-9), null for pitcher in DH lineup"
    )

    @field_validator("position")
    @classmethod
    def validate_position(cls, v: str) -> str:
        """Ensure position is valid"""
        valid = ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "DH"]
        if v not in valid:
            raise ValueError(f"Position must be one of {valid}")
        return v


class SubmitLineupsRequest(BaseModel):
    """
    Request model for submitting lineups for both teams.

    Supports both 9-player (pitcher bats) and 10-player (DH) configurations:
    - 9 players: All have batting orders 1-9
    - 10 players: Pitcher has batting_order=null, others have 1-9
    """

    home_lineup: list[LineupPlayerRequest] = Field(
        ..., min_length=9, max_length=10, description="Home team starting lineup (9-10 players)"
    )
    away_lineup: list[LineupPlayerRequest] = Field(
        ..., min_length=9, max_length=10, description="Away team starting lineup (9-10 players)"
    )

    @field_validator("home_lineup", "away_lineup")
    @classmethod
    def validate_lineup(cls, v: list[LineupPlayerRequest]) -> list[LineupPlayerRequest]:
        """
        Validate lineup structure for 9-player or 10-player (DH) configurations.

        Rules:
        - 9 players: All must have batting orders 1-9
        - 10 players: Exactly 9 have batting orders 1-9, pitcher has null
        """
        lineup_size = len(v)

        # Check batting orders
        batters = [p for p in v if p.batting_order is not None]
        batting_orders = [p.batting_order for p in batters]

        if len(batting_orders) != 9:
            raise ValueError(f"Must have exactly 9 batters with batting orders, got {len(batting_orders)}")

        if set(batting_orders) != {1, 2, 3, 4, 5, 6, 7, 8, 9}:
            raise ValueError("Batting orders must be exactly 1-9 with no duplicates")

        # Check positions
        positions = [p.position for p in v]
        required_positions = ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF"]

        for req_pos in required_positions:
            if req_pos not in positions:
                raise ValueError(f"Missing required position: {req_pos}")

        # For 10-player lineup, must have DH and pitcher must not bat
        if lineup_size == 10:
            if "DH" not in positions:
                raise ValueError("10-player lineup must include DH position")

            pitchers = [p for p in v if p.position == "P"]
            if len(pitchers) != 1:
                raise ValueError("Must have exactly 1 pitcher")

            if pitchers[0].batting_order is not None:
                raise ValueError("Pitcher cannot have batting order in DH lineup")

        # For 9-player lineup, pitcher must bat (no DH)
        elif lineup_size == 9:
            if "DH" in positions:
                raise ValueError("9-player lineup cannot include DH")

            pitchers = [p for p in v if p.position == "P"]
            if len(pitchers) != 1:
                raise ValueError("Must have exactly 1 pitcher")

            if pitchers[0].batting_order is None:
                raise ValueError("Pitcher must have batting order when no DH")

        # Check player uniqueness
        player_ids = [p.player_id for p in v]
        if len(set(player_ids)) != len(player_ids):
            raise ValueError("Players cannot be duplicated in lineup")

        return v


class SubmitLineupsResponse(BaseModel):
    """Response model for lineup submission"""

    game_id: str
    message: str
    home_lineup_count: int
    away_lineup_count: int


@router.get("/", response_model=list[GameListItem])
async def list_games():
    """
    List all games from the database with enriched team and game state info.

    Returns game information including team names, scores, and current inning.
    TODO: Add user filtering, pagination, and more sophisticated queries
    """
    try:
        logger.info("Fetching games list from database")

        # Get all games from database (for now - later we can add filters)
        from app.database.session import AsyncSessionLocal
        from app.models.db_models import Game

        async with AsyncSessionLocal() as session:
            from sqlalchemy import select
            result = await session.execute(
                select(Game).order_by(Game.created_at.desc())
            )
            games = result.scalars().all()

            # Collect unique team IDs for batch lookup
            team_ids = set()
            for game in games:
                team_ids.add(game.home_team_id)
                team_ids.add(game.away_team_id)

            # Fetch team data (uses cache)
            teams_data = await sba_api_client.get_teams_by_ids(list(team_ids))

            # Convert to response model with enriched data
            game_list = []
            for game in games:
                home_team = teams_data.get(game.home_team_id, {})
                away_team = teams_data.get(game.away_team_id, {})

                game_list.append(
                    GameListItem(
                        game_id=str(game.id),
                        league_id=game.league_id,
                        status=game.status,
                        home_team_id=game.home_team_id,
                        away_team_id=game.away_team_id,
                        home_team_name=home_team.get("lname"),
                        away_team_name=away_team.get("lname"),
                        home_team_abbrev=home_team.get("abbrev"),
                        away_team_abbrev=away_team.get("abbrev"),
                        home_score=game.home_score or 0,
                        away_score=game.away_score or 0,
                        inning=game.current_inning,
                        half=game.current_half,
                        schedule_game_id=game.schedule_game_id,
                    )
                )

            logger.info(f"Retrieved {len(game_list)} games from database")
            return game_list

    except Exception as e:
        logger.exception(f"Failed to list games: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to list games: {str(e)}")


@router.get("/{game_id}")
async def get_game(game_id: str):
    """
    Get game details including team IDs and status.

    Args:
        game_id: Game identifier

    Returns:
        Game information including home/away team IDs

    Raises:
        400: Invalid game_id format
        404: Game not found
    """
    try:
        # Validate game_id format
        try:
            game_uuid = UUID(game_id)
        except ValueError:
            raise HTTPException(status_code=400, detail="Invalid game_id format")

        logger.info(f"Fetching game details for {game_id}")

        # Try to get game from state manager first (in-memory)
        state = state_manager.get_state(game_uuid)
        if state:
            return {
                "game_id": game_id,
                "status": state.status,
                "home_team_id": state.home_team_id,
                "away_team_id": state.away_team_id,
                "league_id": state.league_id,
            }

        # Fallback: Load from database if not in memory
        logger.info(f"Game {game_id} not in memory, loading from database")
        from app.database.session import AsyncSessionLocal
        from app.models.db_models import Game

        async with AsyncSessionLocal() as session:
            from sqlalchemy import select
            result = await session.execute(
                select(Game).where(Game.id == game_uuid)
            )
            game = result.scalar_one_or_none()

            if not game:
                raise HTTPException(status_code=404, detail=f"Game {game_id} not found")

            return {
                "game_id": game_id,
                "status": game.status,
                "home_team_id": game.home_team_id,
                "away_team_id": game.away_team_id,
                "league_id": game.league_id,
            }

    except HTTPException:
        raise
    except Exception as e:
        logger.exception(f"Failed to fetch game {game_id}: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to fetch game: {str(e)}")


@router.post("/", response_model=CreateGameResponse)
async def create_game(request: CreateGameRequest):
    """
    Create new game

    Creates a new game in the state manager and database.
    Note: Lineups must be added separately before the game can be started.
    """
    try:
        # Generate game ID
        game_id = uuid4()
        logger.info(
            f"Creating game {game_id}: {request.home_team_id} vs {request.away_team_id}"
        )

        # Validate teams are different
        if request.home_team_id == request.away_team_id:
            raise HTTPException(
                status_code=400, detail="Home and away teams must be different"
            )

        # Fetch team display info from SBA API and build game_metadata
        # This persists team data so it's always available regardless of season
        teams_data = await sba_api_client.get_teams_by_ids(
            [request.home_team_id, request.away_team_id], season=request.season
        )

        # Validate that we successfully fetched both teams
        missing_teams = [
            tid for tid in [request.home_team_id, request.away_team_id]
            if tid not in teams_data
        ]
        if missing_teams:
            logger.error(
                f"Failed to fetch team data for IDs {missing_teams} in season {request.season}"
            )
            raise HTTPException(
                status_code=400,
                detail=f"Could not fetch team data for team IDs: {missing_teams}. "
                       f"Verify teams exist in season {request.season}."
            )

        home_team_data = teams_data.get(request.home_team_id, {})
        away_team_data = teams_data.get(request.away_team_id, {})

        game_metadata = {
            "home_team": {
                "lname": home_team_data.get("lname"),
                "sname": home_team_data.get("sname"),
                "abbrev": home_team_data.get("abbrev"),
                "color": home_team_data.get("color"),
                "thumbnail": home_team_data.get("thumbnail"),
            },
            "away_team": {
                "lname": away_team_data.get("lname"),
                "sname": away_team_data.get("sname"),
                "abbrev": away_team_data.get("abbrev"),
                "color": away_team_data.get("color"),
                "thumbnail": away_team_data.get("thumbnail"),
            },
        }

        # Create game in state manager (in-memory)
        state = await state_manager.create_game(
            game_id=game_id,
            league_id=request.league_id,
            home_team_id=request.home_team_id,
            away_team_id=request.away_team_id,
            game_metadata=game_metadata,
        )

        # Save to database
        db_ops = DatabaseOperations()
        await db_ops.create_game(
            game_id=game_id,
            league_id=request.league_id,
            home_team_id=request.home_team_id,
            away_team_id=request.away_team_id,
            game_mode="friendly" if not request.is_ai_opponent else "ai",
            visibility="public",
            game_metadata=game_metadata,
        )

        logger.info(
            f"Game {game_id} created successfully - status: {state.status}"
        )

        return CreateGameResponse(
            game_id=str(game_id),
            message=f"Game '{request.name}' created successfully. Add lineups to start the game.",
            status=state.status,
        )

    except HTTPException:
        raise
    except Exception as e:
        logger.exception(f"Failed to create game: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to create game: {str(e)}")


@router.post("/quick-create", response_model=CreateGameResponse)
async def quick_create_game(
    request: QuickCreateRequest | None = None,
    user: dict | None = Depends(get_current_user_optional),
):
    """
    Quick-create endpoint for testing - creates a game with auto-generated lineups.

    If home_team_id and away_team_id are provided, fetches rosters from SBA API
    and auto-generates lineups. Otherwise uses default teams (35 vs 38) with
    pre-configured lineups.

    The authenticated user's discord_id is stored as creator_discord_id, allowing
    them to control teams regardless of actual team ownership.

    Args:
        request: Optional request body with custom team IDs

    Returns:
        CreateGameResponse with game_id
    """
    try:
        # Generate game ID
        game_id = uuid4()
        league_id = "sba"

        # Determine team IDs and schedule link
        use_custom_teams = (
            request is not None
            and request.home_team_id is not None
            and request.away_team_id is not None
        )

        if use_custom_teams:
            home_team_id = request.home_team_id
            away_team_id = request.away_team_id
            schedule_game_id = request.schedule_game_id
        else:
            # Default demo teams
            home_team_id = 35
            away_team_id = 38
            schedule_game_id = None

        # Get creator's discord_id from authenticated user
        creator_discord_id = user.get("discord_id") if user else None

        logger.info(
            f"Quick-creating game {game_id}: {home_team_id} vs {away_team_id} "
            f"(creator: {creator_discord_id}, custom_teams: {use_custom_teams})"
        )

        # Fetch team display info from SBA API and build game_metadata
        # This persists team data so it's always available regardless of season
        teams_data = await sba_api_client.get_teams_by_ids(
            [home_team_id, away_team_id], season=13
        )

        # Validate that we successfully fetched both teams
        missing_teams = [
            tid for tid in [home_team_id, away_team_id]
            if tid not in teams_data
        ]
        if missing_teams:
            logger.error(
                f"Quick-create: Failed to fetch team data for IDs {missing_teams}"
            )
            raise HTTPException(
                status_code=400,
                detail=f"Could not fetch team data for team IDs: {missing_teams}. "
                       f"Verify teams exist in season 13."
            )

        home_team_data = teams_data.get(home_team_id, {})
        away_team_data = teams_data.get(away_team_id, {})

        game_metadata = {
            "home_team": {
                "lname": home_team_data.get("lname"),
                "sname": home_team_data.get("sname"),
                "abbrev": home_team_data.get("abbrev"),
                "color": home_team_data.get("color"),
                "thumbnail": home_team_data.get("thumbnail"),
            },
            "away_team": {
                "lname": away_team_data.get("lname"),
                "sname": away_team_data.get("sname"),
                "abbrev": away_team_data.get("abbrev"),
                "color": away_team_data.get("color"),
                "thumbnail": away_team_data.get("thumbnail"),
            },
        }

        # Create game in state manager
        state = await state_manager.create_game(
            game_id=game_id,
            league_id=league_id,
            home_team_id=home_team_id,
            away_team_id=away_team_id,
            creator_discord_id=creator_discord_id,
            game_metadata=game_metadata,
        )

        # Save to database
        db_ops = DatabaseOperations()
        await db_ops.create_game(
            game_id=game_id,
            league_id=league_id,
            home_team_id=home_team_id,
            away_team_id=away_team_id,
            game_mode="friendly",
            visibility="public",
            schedule_game_id=schedule_game_id,
            game_metadata=game_metadata,
        )

        if use_custom_teams:
            # Fetch rosters from SBA API and build lineups dynamically
            await _build_lineup_from_roster(game_id, home_team_id, season=13)
            await _build_lineup_from_roster(game_id, away_team_id, season=13)
        else:
            # Use pre-configured demo lineups
            # Submit home lineup (Team 35)
            home_lineup_data = [
                {"player_id": 1417, "position": "C", "batting_order": 1},
                {"player_id": 1186, "position": "1B", "batting_order": 2},
                {"player_id": 1381, "position": "2B", "batting_order": 3},
                {"player_id": 1576, "position": "3B", "batting_order": 4},
                {"player_id": 1242, "position": "SS", "batting_order": 5},
                {"player_id": 1600, "position": "LF", "batting_order": 6},
                {"player_id": 1675, "position": "CF", "batting_order": 7},
                {"player_id": 1700, "position": "RF", "batting_order": 8},
                {"player_id": 1759, "position": "DH", "batting_order": 9},
                {"player_id": 1948, "position": "P", "batting_order": None},
            ]

            for player in home_lineup_data:
                await lineup_service.add_sba_player_to_lineup(
                    game_id=game_id,
                    team_id=home_team_id,
                    player_id=player["player_id"],
                    position=player["position"],
                    batting_order=player["batting_order"],
                    is_starter=True,
                )

            # Submit away lineup (Team 38)
            away_lineup_data = [
                {"player_id": 1080, "position": "C", "batting_order": 1},
                {"player_id": 1148, "position": "1B", "batting_order": 2},
                {"player_id": 1166, "position": "2B", "batting_order": 3},
                {"player_id": 1513, "position": "3B", "batting_order": 4},
                {"player_id": 1209, "position": "SS", "batting_order": 5},
                {"player_id": 1735, "position": "LF", "batting_order": 6},
                {"player_id": 1665, "position": "CF", "batting_order": 7},
                {"player_id": 1961, "position": "RF", "batting_order": 8},
                {"player_id": 1980, "position": "DH", "batting_order": 9},
                {"player_id": 2005, "position": "P", "batting_order": None},
            ]

            for player in away_lineup_data:
                await lineup_service.add_sba_player_to_lineup(
                    game_id=game_id,
                    team_id=away_team_id,
                    player_id=player["player_id"],
                    position=player["position"],
                    batting_order=player["batting_order"],
                    is_starter=True,
                )

        # Start the game
        await game_engine.start_game(game_id)

        logger.info(f"Quick-created game {game_id} and started successfully")

        return CreateGameResponse(
            game_id=str(game_id),
            message=f"Game quick-created with Team {home_team_id} vs Team {away_team_id}. Ready to play!",
            status="active",
        )

    except Exception as e:
        logger.exception(f"Failed to quick-create game: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to quick-create game: {str(e)}")


async def _build_lineup_from_roster(game_id: UUID, team_id: int, season: int = 13) -> None:
    """
    Build a lineup from SBA API roster data.

    Fetches the team roster and assigns players to positions based on their
    listed positions. Uses a simple algorithm to fill all positions:
    - First player at each position gets assigned
    - DH filled with extra player if available
    - Batting order assigned by position priority

    Args:
        game_id: Game UUID
        team_id: Team ID to fetch roster for
        season: Season number (default 13)
    """
    # Fetch roster from SBA API
    roster = await sba_api_client.get_roster(team_id=team_id, season=season)

    if not roster:
        raise HTTPException(
            status_code=400,
            detail=f"No roster found for team {team_id} in season {season}",
        )

    # Position priority for lineup building
    # Standard DH lineup: 9 fielders + DH, pitcher doesn't bat
    positions_needed = ["C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "P"]
    lineup_positions: dict[str, dict] = {}
    used_player_ids: set[int] = set()

    # First pass: assign players to their primary positions
    for player in roster:
        player_id = player.get("id")
        player_positions = player.get("position", "").split("/")

        if not player_id or player_id in used_player_ids:
            continue

        # Try to assign to first matching needed position
        for pos in player_positions:
            pos = pos.strip().upper()
            if pos in positions_needed and pos not in lineup_positions:
                lineup_positions[pos] = {
                    "player_id": player_id,
                    "position": pos,
                }
                used_player_ids.add(player_id)
                break

    # Check we have all required positions
    missing = [p for p in positions_needed if p not in lineup_positions]
    if missing:
        # Second pass: try to fill missing positions with any available player
        for pos in missing:
            for player in roster:
                player_id = player.get("id")
                if player_id and player_id not in used_player_ids:
                    lineup_positions[pos] = {
                        "player_id": player_id,
                        "position": pos,
                    }
                    used_player_ids.add(player_id)
                    break

    # Still missing? Raise error
    still_missing = [p for p in positions_needed if p not in lineup_positions]
    if still_missing:
        raise HTTPException(
            status_code=400,
            detail=f"Could not fill positions {still_missing} for team {team_id}",
        )

    # Find DH - use next available player not in lineup
    dh_player = None
    for player in roster:
        player_id = player.get("id")
        if player_id and player_id not in used_player_ids:
            dh_player = {"player_id": player_id, "position": "DH"}
            used_player_ids.add(player_id)
            break

    if not dh_player:
        raise HTTPException(
            status_code=400,
            detail=f"Not enough players for DH on team {team_id}",
        )

    # Build final lineup with batting order
    # Batting order: C, 1B, 2B, 3B, SS, LF, CF, RF, DH
    batting_order_positions = ["C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "DH"]

    for order, pos in enumerate(batting_order_positions, start=1):
        if pos == "DH":
            player_data = dh_player
        else:
            player_data = lineup_positions[pos]

        await lineup_service.add_sba_player_to_lineup(
            game_id=game_id,
            team_id=team_id,
            player_id=player_data["player_id"],
            position=player_data["position"],
            batting_order=order,
            is_starter=True,
        )

    # Add pitcher (no batting order in DH lineup)
    pitcher_data = lineup_positions["P"]
    await lineup_service.add_sba_player_to_lineup(
        game_id=game_id,
        team_id=team_id,
        player_id=pitcher_data["player_id"],
        position="P",
        batting_order=None,
        is_starter=True,
    )

    logger.info(f"Built lineup for team {team_id} with {len(batting_order_positions) + 1} players")


@router.post("/{game_id}/lineups", response_model=SubmitLineupsResponse)
async def submit_lineups(game_id: str, request: SubmitLineupsRequest):
    """
    Submit lineups for both teams.

    Accepts complete lineups for home and away teams. Supports both:
    - 9-player lineups (pitcher bats, no DH)
    - 10-player lineups (universal DH, pitcher doesn't bat)

    Args:
        game_id: Game identifier
        request: Lineup data for both teams

    Returns:
        Confirmation with lineup counts

    Raises:
        400: Invalid lineup structure or validation error
        404: Game not found
        500: Database or API error
    """
    try:
        # Validate game_id format
        try:
            game_uuid = UUID(game_id)
        except ValueError:
            raise HTTPException(status_code=400, detail="Invalid game_id format")

        logger.info(f"Submitting lineups for game {game_id}")

        # Get game state from memory or recover from database
        state = state_manager.get_state(game_uuid)
        if not state:
            logger.info(f"Game {game_id} not in memory, recovering from database")

            # Use recover_game to properly load game state
            state = await state_manager.recover_game(game_uuid)

            if not state:
                raise HTTPException(status_code=404, detail=f"Game {game_id} not found")

            logger.info(f"Recovered game {game_id} from database")

        # Process home team lineup
        home_count = 0
        for player in request.home_lineup:
            await lineup_service.add_sba_player_to_lineup(
                game_id=game_uuid,
                team_id=state.home_team_id,
                player_id=player.player_id,
                position=player.position,
                batting_order=player.batting_order,
                is_starter=True,
            )
            home_count += 1

        logger.info(f"Added {home_count} players to home team lineup")

        # Process away team lineup
        away_count = 0
        for player in request.away_lineup:
            await lineup_service.add_sba_player_to_lineup(
                game_id=game_uuid,
                team_id=state.away_team_id,
                player_id=player.player_id,
                position=player.position,
                batting_order=player.batting_order,
                is_starter=True,
            )
            away_count += 1

        logger.info(f"Added {away_count} players to away team lineup")

        # Automatically start the game after lineups are submitted
        from app.core.game_engine import game_engine
        try:
            await game_engine.start_game(game_uuid)
            logger.info(f"Game {game_id} started successfully after lineup submission")
        except Exception as e:
            logger.warning(f"Failed to auto-start game {game_id}: {e}")
            # Don't fail the lineup submission if game start fails
            # User can manually start if needed

        return SubmitLineupsResponse(
            game_id=game_id,
            message=f"Lineups submitted successfully. Home: {home_count} players, Away: {away_count} players.",
            home_lineup_count=home_count,
            away_lineup_count=away_count,
        )

    except HTTPException:
        raise
    except Exception as e:
        logger.exception(f"Failed to submit lineups for game {game_id}: {e}")
        raise HTTPException(
            status_code=500, detail=f"Failed to submit lineups: {str(e)}"
        )


class BenchPlayerRequest(BaseModel):
    """Single bench player in lineup request"""

    player_id: int = Field(..., description="SBA player ID")


class SubmitTeamLineupRequest(BaseModel):
    """Request model for submitting a single team's lineup"""

    team_id: int = Field(..., description="Team ID submitting the lineup")
    lineup: list[LineupPlayerRequest] = Field(
        ..., min_length=9, max_length=10, description="Team's starting lineup (9-10 players)"
    )
    bench: list[BenchPlayerRequest] = Field(
        default_factory=list, description="Bench players (not in starting lineup)"
    )

    @field_validator("lineup")
    @classmethod
    def validate_lineup(cls, v: list[LineupPlayerRequest]) -> list[LineupPlayerRequest]:
        """Validate lineup structure - same rules as SubmitLineupsRequest"""
        lineup_size = len(v)

        # Check batting orders
        batters = [p for p in v if p.batting_order is not None]
        batting_orders = [p.batting_order for p in batters]

        if len(batting_orders) != 9:
            raise ValueError(f"Must have exactly 9 batters with batting orders, got {len(batting_orders)}")

        if set(batting_orders) != {1, 2, 3, 4, 5, 6, 7, 8, 9}:
            raise ValueError("Batting orders must be exactly 1-9 with no duplicates")

        # Check positions
        positions = [p.position for p in v]
        required_positions = ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF"]

        for req_pos in required_positions:
            if req_pos not in positions:
                raise ValueError(f"Missing required position: {req_pos}")

        # For 10-player lineup, must have DH and pitcher must not bat
        if lineup_size == 10:
            if "DH" not in positions:
                raise ValueError("10-player lineup must include DH position")

            pitchers = [p for p in v if p.position == "P"]
            if len(pitchers) != 1:
                raise ValueError("Must have exactly 1 pitcher")

            if pitchers[0].batting_order is not None:
                raise ValueError("Pitcher cannot have batting order in DH lineup")

        # For 9-player lineup, pitcher must bat (no DH)
        elif lineup_size == 9:
            if "DH" in positions:
                raise ValueError("9-player lineup cannot include DH")

            pitchers = [p for p in v if p.position == "P"]
            if len(pitchers) != 1:
                raise ValueError("Must have exactly 1 pitcher")

            if pitchers[0].batting_order is None:
                raise ValueError("Pitcher must have batting order when no DH")

        # Check player uniqueness
        player_ids = [p.player_id for p in v]
        if len(set(player_ids)) != len(player_ids):
            raise ValueError("Players cannot be duplicated in lineup")

        return v


class SubmitTeamLineupResponse(BaseModel):
    """Response model for single team lineup submission"""

    game_id: str
    team_id: int
    message: str
    lineup_count: int
    game_ready: bool  # True if both teams have submitted and game can start
    game_started: bool  # True if game was auto-started


class LineupPlayerInfo(BaseModel):
    """Individual player in a lineup"""
    player_id: int
    position: str
    batting_order: int | None


class LineupStatusResponse(BaseModel):
    """Response model for lineup status check"""

    game_id: str
    home_team_id: int
    away_team_id: int
    home_lineup_submitted: bool
    away_lineup_submitted: bool
    home_lineup_count: int
    away_lineup_count: int
    game_status: str
    # Include actual lineup data for display
    home_lineup: list[LineupPlayerInfo] = []
    away_lineup: list[LineupPlayerInfo] = []


@router.get("/{game_id}/lineup-status", response_model=LineupStatusResponse)
async def get_lineup_status(game_id: str):
    """
    Check lineup submission status for a game.

    Returns whether each team has submitted their lineup.
    Works for pending games (before game state is fully initialized).
    """
    try:
        # Validate game_id format
        try:
            game_uuid = UUID(game_id)
        except ValueError:
            raise HTTPException(status_code=400, detail="Invalid game_id format")

        # First check in-memory state (if game is active)
        state = state_manager.get_state(game_uuid)
        if state:
            # Game is in memory, use state_manager for lineup info
            home_lineup = state_manager.get_lineup(game_uuid, state.home_team_id)
            away_lineup = state_manager.get_lineup(game_uuid, state.away_team_id)

            home_count = len(home_lineup.players) if home_lineup else 0
            away_count = len(away_lineup.players) if away_lineup else 0

            # Convert to response format (card_id is player_id for SBA)
            home_lineup_data = [
                LineupPlayerInfo(
                    player_id=p.card_id,
                    position=p.position,
                    batting_order=p.batting_order
                )
                for p in (home_lineup.players if home_lineup else [])
            ]
            away_lineup_data = [
                LineupPlayerInfo(
                    player_id=p.card_id,
                    position=p.position,
                    batting_order=p.batting_order
                )
                for p in (away_lineup.players if away_lineup else [])
            ]

            # Safeguard: Auto-start game if both lineups ready but game still pending
            game_status = state.status
            if home_count >= 9 and away_count >= 9 and game_status == "pending":
                try:
                    logger.info(f"Auto-starting game {game_id} (in-memory) - both lineups ready but game was pending")
                    await game_engine.start_game(game_uuid)
                    game_status = "active"
                except Exception as start_error:
                    logger.warning(f"Failed to auto-start game {game_id}: {start_error}")

            return LineupStatusResponse(
                game_id=game_id,
                home_team_id=state.home_team_id,
                away_team_id=state.away_team_id,
                home_lineup_submitted=home_count >= 9,
                away_lineup_submitted=away_count >= 9,
                home_lineup_count=home_count,
                away_lineup_count=away_count,
                game_status=game_status,
                home_lineup=home_lineup_data,
                away_lineup=away_lineup_data,
            )

        # Game not in memory - query database directly
        # This works for pending games where GameState can't be fully constructed
        db_ops = DatabaseOperations()
        game = await db_ops.get_game(game_uuid)
        if not game:
            raise HTTPException(status_code=404, detail=f"Game {game_id} not found")

        # Get lineup data from database
        home_lineups = await db_ops.get_active_lineup(game_uuid, game.home_team_id)
        away_lineups = await db_ops.get_active_lineup(game_uuid, game.away_team_id)

        home_count = len(home_lineups)
        away_count = len(away_lineups)

        # Convert to response format
        home_lineup_data = [
            LineupPlayerInfo(
                player_id=lineup.player_id,
                position=lineup.position,
                batting_order=lineup.batting_order
            )
            for lineup in home_lineups
            if lineup.player_id is not None  # SBA uses player_id
        ]
        away_lineup_data = [
            LineupPlayerInfo(
                player_id=lineup.player_id,
                position=lineup.position,
                batting_order=lineup.batting_order
            )
            for lineup in away_lineups
            if lineup.player_id is not None
        ]

        # Safeguard: Auto-start game if both lineups are ready but game is still pending
        # This handles edge cases where lineups were submitted before the auto-start fix
        game_status = game.status
        if home_count >= 9 and away_count >= 9 and game_status == "pending":
            try:
                logger.info(f"Auto-starting game {game_id} - both lineups ready but game was pending")
                # First recover game into state manager (required before start_game)
                await state_manager.recover_game(game_uuid)
                await game_engine.start_game(game_uuid)
                game_status = "active"
            except Exception as start_error:
                logger.warning(f"Failed to auto-start game {game_id}: {start_error}")
                # Continue with pending status - user can retry

        return LineupStatusResponse(
            game_id=game_id,
            home_team_id=game.home_team_id,
            away_team_id=game.away_team_id,
            home_lineup_submitted=home_count >= 9,
            away_lineup_submitted=away_count >= 9,
            home_lineup_count=home_count,
            away_lineup_count=away_count,
            game_status=game_status,
            home_lineup=home_lineup_data,
            away_lineup=away_lineup_data,
        )

    except HTTPException:
        raise
    except Exception as e:
        logger.exception(f"Failed to get lineup status for game {game_id}: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to get lineup status: {str(e)}")


@router.post("/{game_id}/lineup", response_model=SubmitTeamLineupResponse)
async def submit_team_lineup(game_id: str, request: SubmitTeamLineupRequest):
    """
    Submit lineup for a single team.

    Accepts lineup for one team at a time. Game auto-starts when both teams
    have submitted their lineups.

    Args:
        game_id: Game identifier
        request: Team ID and lineup data

    Returns:
        Confirmation with lineup count and game status

    Raises:
        400: Invalid lineup structure, team not in game, or lineup already submitted
        404: Game not found
        500: Database or API error
    """
    try:
        # Validate game_id format
        try:
            game_uuid = UUID(game_id)
        except ValueError:
            raise HTTPException(status_code=400, detail="Invalid game_id format")

        logger.info(f"Submitting lineup for team {request.team_id} in game {game_id}")

        # Get game state from memory or recover from database
        state = state_manager.get_state(game_uuid)
        if not state:
            logger.info(f"Game {game_id} not in memory, recovering from database")

            # Use recover_game to properly load game state AND existing lineups
            state = await state_manager.recover_game(game_uuid)

            if not state:
                raise HTTPException(status_code=404, detail=f"Game {game_id} not found")

            logger.info(f"Recovered game {game_id} from database")

        # Validate team is part of this game
        if request.team_id not in [state.home_team_id, state.away_team_id]:
            raise HTTPException(
                status_code=400,
                detail=f"Team {request.team_id} is not part of game {game_id}"
            )

        # Check if lineup already submitted for this team
        is_home = request.team_id == state.home_team_id
        existing_lineup = state_manager.get_lineup(game_uuid, request.team_id)

        if existing_lineup and len(existing_lineup.players) > 0:
            raise HTTPException(
                status_code=400,
                detail=f"Lineup already submitted for team {request.team_id}"
            )

        # Step 1: Collect all player IDs (starters + bench) for batch API fetch
        all_player_ids = [p.player_id for p in request.lineup]
        all_player_ids.extend([p.player_id for p in request.bench])

        # Step 2: Fetch all player data from SBA API to get positions
        player_data = {}
        if all_player_ids:
            try:
                player_data = await sba_api_client.get_players_batch(all_player_ids)
                logger.info(f"Fetched {len(player_data)}/{len(all_player_ids)} players from SBA API")
            except Exception as e:
                logger.warning(f"Failed to fetch player data from SBA API: {e}")
                # Continue - roster entries will have empty positions

        # Step 3: Add ALL players to RosterLink with their natural positions and cached data
        db_ops = DatabaseOperations()
        roster_count = 0
        for player_id in all_player_ids:
            player_positions: list[str] = []
            cached_player_data: dict | None = None

            if player_id in player_data:
                player = player_data[player_id]
                player_positions = player.get_positions()
                # Cache essential player data to avoid runtime API calls
                cached_player_data = {
                    "name": player.name,
                    "image": player.get_image_url(),
                    "headshot": player.headshot or "",
                }

            await db_ops.add_sba_roster_player(
                game_id=game_uuid,
                player_id=player_id,
                team_id=request.team_id,
                player_positions=player_positions,
                player_data=cached_player_data,
            )
            roster_count += 1

        logger.info(f"Added {roster_count} players to roster for team {request.team_id}")

        # Step 4: Add only STARTERS to active Lineup table
        player_count = 0
        for player in request.lineup:
            await lineup_service.add_sba_player_to_lineup(
                game_id=game_uuid,
                team_id=request.team_id,
                player_id=player.player_id,
                position=player.position,
                batting_order=player.batting_order,
                is_starter=True,
            )
            player_count += 1

        logger.info(f"Added {player_count} starters to team {request.team_id} lineup")

        # Note: Bench players are NOT added to Lineup - they're derived from
        # RosterLink players not in active Lineup via get_bench_players()

        # Load lineup from DB and cache in state_manager for subsequent checks
        team_lineup = await lineup_service.load_team_lineup_with_player_data(
            game_id=game_uuid,
            team_id=request.team_id,
            league_id=state.league_id,
        )
        if team_lineup:
            state_manager.set_lineup(game_uuid, request.team_id, team_lineup)
            logger.info(f"Cached lineup for team {request.team_id} in state_manager")

        # Check if both teams now have lineups
        home_lineup = state_manager.get_lineup(game_uuid, state.home_team_id)
        away_lineup = state_manager.get_lineup(game_uuid, state.away_team_id)
        home_ready = home_lineup is not None and len(home_lineup.players) >= 9
        away_ready = away_lineup is not None and len(away_lineup.players) >= 9
        game_ready = home_ready and away_ready

        game_started = False
        if game_ready:
            # Auto-start the game
            from app.core.game_engine import game_engine
            try:
                await game_engine.start_game(game_uuid)
                game_started = True
                logger.info(f"Game {game_id} auto-started after both lineups submitted")
            except Exception as e:
                logger.warning(f"Failed to auto-start game {game_id}: {e}")

        team_type = "home" if is_home else "away"
        other_team = "away" if is_home else "home"

        if game_started:
            message = f"{team_type.capitalize()} lineup submitted. Game started!"
        elif game_ready:
            message = f"{team_type.capitalize()} lineup submitted. Both teams ready."
        else:
            message = f"{team_type.capitalize()} lineup submitted. Waiting for {other_team} team."

        return SubmitTeamLineupResponse(
            game_id=game_id,
            team_id=request.team_id,
            message=message,
            lineup_count=player_count,
            game_ready=game_ready,
            game_started=game_started,
        )

    except HTTPException:
        raise
    except Exception as e:
        logger.exception(f"Failed to submit lineup for team {request.team_id} in game {game_id}: {e}")
        raise HTTPException(
            status_code=500, detail=f"Failed to submit lineup: {str(e)}"
        )
