# backend/app/crud.py
from uuid import uuid4

from sqlalchemy.orm import Session, joinedload

from . import logic, models, schemas


def _rebuild_structure(db: Session, t: models.Tournament):
    for m in t.matches:
        db.delete(m)
    db.flush()

    logic.generate_bracket(db, t)
    db.flush()
    db.refresh(t)
    logic.update_schedule_times(db, t)


def create_tournament(db: Session, data: schemas.TournamentCreate):
    t_id = str(uuid4())[:8]
    new_t = models.Tournament(
        id=t_id,
        name=data.name,
        code=data.code,
        timestamp=data.timestamp,
        duration=data.duration,
        type=data.type,
    )
    new_t.teams = [models.Team(name=n) for n in data.teams]
    new_t.courts = db.query(models.Court).filter(models.Court.id.in_(data.courts)).all()

    db.add(new_t)
    db.commit()

    # Generate Bracket
    logic.generate_bracket(db, new_t)
    logic.update_schedule_times(db, new_t)

    db.commit()
    db.refresh(new_t)
    return new_t


def get_tournaments(db: Session):
    return db.query(models.Tournament).all()

def get_tournament(db: Session, tournament_id: str, lock: bool = False):
    query = db.query(models.Tournament).filter(models.Tournament.id == tournament_id)
    if lock:
        query = query.with_for_update() 
    return query.first()

def delete_tournament(db: Session, tournament_id: str) -> bool:
    t = get_tournament(db, tournament_id, lock=True)
    if not t:
        return False
    db.delete(t)
    db.commit()
    return True


def update_tournament_details(
    db: Session, tournament_id: str, data: schemas.TournamentUpdate
):
    t = get_tournament(db, tournament_id, lock=True)
    if not t:
        return None

    update_data = data.model_dump(exclude_unset=True)
    incoming_type = update_data.get("type")
    type_changed = incoming_type and incoming_type != t.type

    for key, value in update_data.items():
        setattr(t, key, value)

    if type_changed:
        _rebuild_structure(db, t)
    else:
        logic.update_schedule_times(db, t)

    db.commit()
    db.refresh(t)
    return t


def get_teams(db: Session, tournament_id: str):
    return (
        db.query(models.Team).filter(models.Team.tournament_id == tournament_id).all()
    )


def create_team(db: Session, tournament_id: str, team_data: schemas.TeamCreate):
    t = get_tournament(db, tournament_id, lock=True)
    if not t:
        return None

    new_team = models.Team(name=team_data.name, tournament_id=tournament_id)
    db.add(new_team)
    db.flush()

    _rebuild_structure(db, t)

    db.commit()
    db.refresh(new_team)
    return new_team


def update_tournament_teams(db: Session, tournament_id: str, new_team_names: list[str]):
    t = get_tournament(db, tournament_id, lock=True)
    if not t:
        return None

    t.teams = [models.Team(name=n, tournament_id=t.id) for n in new_team_names]
    db.flush()

    _rebuild_structure(db, t)

    db.commit()
    db.refresh(t)
    return t


def delete_team(db: Session, tournament_id: str, team_id: int):
    t = get_tournament(db, tournament_id, lock=True)
    if not t:
        return None
    team = db.get(models.Team, team_id)
    if not team or team.tournament_id != tournament_id:
        return None

    db.delete(team)
    db.flush()

    _rebuild_structure(db, t)

    db.commit()
    return True


def get_courts(db: Session):
    return db.query(models.Court).all()


def create_court(db: Session, court_data: schemas.CourtCreate):
    new_court = models.Court(name=court_data.name)
    db.add(new_court)
    db.commit()
    db.refresh(new_court)
    return new_court


def delete_court(db: Session, court_id: int):
    court = db.get(models.Court, court_id)
    if not court:
        return False

    affected_tournaments = list(court.tournaments)

    matches = db.query(models.Match).filter(models.Match.court_id == court_id).all()
    for m in matches:
        if m.status == models.MatchStatus.FINISHED:
            m.court_id = None
        else:
            m.court_id = None
            m.start_time = None

    db.delete(court)
    db.commit()

    processed_days = set()
    for t in affected_tournaments:
        day = t.timestamp.date() if t.timestamp else None
        if day and day not in processed_days:
            logic.update_schedule_times(db, t)
            processed_days.add(day)

    return True


def update_tournament_courts(db: Session, tournament_id: str, new_court_ids: list[int]):
    t = get_tournament(db, tournament_id, lock=True) 
    if not t:
        return None

    t.courts = db.query(models.Court).filter(models.Court.id.in_(new_court_ids)).all()
    db.flush()
    logic.update_schedule_times(db, t)

    db.commit()
    db.refresh(t)
    return t


def get_tournament_matches(db: Session, tournament_id: str):
    return (
        db.query(models.Match).filter(models.Match.tournament_id == tournament_id).all()
    )


def get_match(db: Session, match_id: str):
    return (
        db.query(models.Match)
        .options(
            joinedload(models.Match.tournament).joinedload(models.Tournament.matches)
        )
        .filter(models.Match.id == match_id)
        .first()
    )
