"""Recompute ratings deterministically from stored race results."""

from datetime import date

from sqlalchemy.orm import Session, joinedload

from packages.core.common.logging import get_logger
from packages.core.ratings.engine import RatingEngine
from packages.core.storage.models import Race, RatingSnapshot, Starter
from packages.core.storage.repositories import RaceRepository, RatingSnapshotRepository

logger = get_logger(__name__)


def recompute_ratings(
    session: Session,
    date_from: date,
    date_to: date,
    clear_existing: bool = False,
    learn_adjustments: bool = False,
) -> int:
    """Recompute all ratings from scratch in date range.

    This is deterministic: same inputs will always produce same outputs.

    Args:
        session: Database session
        date_from: Start date (inclusive)
        date_to: End date (inclusive)
        clear_existing: If True, delete existing rating snapshots first
        learn_adjustments: If True, learn barrier/handicap adjustments from results

    Returns:
        Number of rating snapshots created
    """
    logger.info(f"Recomputing ratings from {date_from} to {date_to}")

    # Clear existing snapshots if requested
    if clear_existing:
        logger.info("Clearing existing rating snapshots...")
        deleted = session.query(RatingSnapshot).delete()
        session.commit()
        logger.info(f"Deleted {deleted} existing rating snapshots")

    # Initialize rating engine with database session
    engine = RatingEngine(db_session=session)

    # Get all races in chronological order
    races = RaceRepository.get_races_for_recompute(session, date_from, date_to)
    logger.info(f"Processing {len(races)} races")

    snapshot_count = 0

    for idx, race in enumerate(races, 1):
        if idx % 100 == 0:
            logger.info(
                f"Processed {idx}/{len(races)} races, {snapshot_count} snapshots"
            )

        # Get starters for race with all relationships loaded
        starters = (
            session.query(Starter)
            .filter(Starter.race_id == race.id)
            .options(
                joinedload(Starter.horse),
                joinedload(Starter.driver),
                joinedload(Starter.trainer),
            )
            .all()
        )

        if not starters:
            logger.debug(f"Skipping race {race.id} - no starters")
            continue

        # Load race.meeting for venue access (for effective rating computation)
        if not race.meeting:
            session.refresh(race, ["meeting"])

        # Process race and get updates
        updates = engine.process_race(race, starters)

        # Learn adjustments if enabled
        if learn_adjustments:
            engine.learn_adjustments_from_race(race, starters, use_global_only=True)

        # Save rating snapshots
        for update in updates:
            RatingSnapshotRepository.upsert(
                session,
                entity_type=update.entity_type,
                entity_id=update.entity_id,
                as_of_race_id=race.id,
                rating=update.new_rating,
                rd=update.rd,
                meta=update.meta,
            )
            snapshot_count += 1

        # Commit periodically
        if idx % 50 == 0:
            session.commit()

    # Final commit
    session.commit()

    logger.info(
        f"Recompute complete: processed {len(races)} races, "
        f"created {snapshot_count} rating snapshots"
    )

    return snapshot_count


def recompute_ratings_incremental(
    session: Session,
    race_id: int,
    engine: RatingEngine | None = None,
    learn_adjustments: bool = False,
) -> int:
    """Recompute ratings for a single race (incremental update).

    This loads existing ratings before the race and updates them.

    Args:
        session: Database session
        race_id: Race ID to process
        engine: Existing rating engine (optional, will create if not provided)
        learn_adjustments: If True, learn adjustments from this race

    Returns:
        Number of rating snapshots created
    """
    if engine is None:
        engine = RatingEngine(db_session=session)

    # Load race with relationships
    race = (
        session.query(Race)
        .options(joinedload(Race.meeting))
        .filter(Race.id == race_id)
        .one()
    )

    # Get starters
    starters = (
        session.query(Starter)
        .filter(Starter.race_id == race_id)
        .options(
            joinedload(Starter.horse),
            joinedload(Starter.driver),
            joinedload(Starter.trainer),
        )
        .all()
    )

    if not starters:
        logger.debug(f"No starters for race {race_id}")
        return 0

    # Load current ratings for all entities involved
    for starter in starters:
        if starter.horse_id:
            _load_entity_rating(session, engine, "HORSE", starter.horse_id, race_id)
        if starter.driver_id:
            _load_entity_rating(session, engine, "DRIVER", starter.driver_id, race_id)
        if starter.trainer_id:
            _load_entity_rating(session, engine, "TRAINER", starter.trainer_id, race_id)

    # Process race
    updates = engine.process_race(race, starters)

    # Learn adjustments if enabled
    if learn_adjustments:
        engine.learn_adjustments_from_race(race, starters, use_global_only=True)

    # Save snapshots
    snapshot_count = 0
    for update in updates:
        RatingSnapshotRepository.upsert(
            session,
            entity_type=update.entity_type,
            entity_id=update.entity_id,
            as_of_race_id=race_id,
            rating=update.new_rating,
            rd=update.rd,
            meta=update.meta,
        )
        snapshot_count += 1

    session.commit()

    return snapshot_count


def _load_entity_rating(
    session: Session,
    engine: RatingEngine,
    entity_type: str,
    entity_id: int,
    before_race_id: int,
) -> None:
    """Load entity's latest rating into engine state.

    Args:
        session: Database session
        engine: Rating engine
        entity_type: Type of entity
        entity_id: Entity ID
        before_race_id: Load rating before this race
    """
    from packages.core.storage.models import EntityType

    entity_enum = EntityType[entity_type]

    snapshot = RatingSnapshotRepository.get_latest_rating(
        session, entity_enum, entity_id, before_race_id
    )

    if snapshot:
        engine.load_rating_state(
            entity_enum,
            entity_id,
            snapshot.rating,
            snapshot.rd,
        )
