from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from datetime import datetime from ..database import get_db from ..models import Match, Player, Court, Stage from ..schemas import MatchScoreUpdate, MatchCourtAssign router = APIRouter(prefix="/api/matches", tags=["matches"]) def _match_detail(match: Match, db: Session) -> dict: p1 = db.query(Player).filter(Player.id == match.player1_id).first() if match.player1_id else None p2 = db.query(Player).filter(Player.id == match.player2_id).first() if match.player2_id else None court = db.query(Court).filter(Court.id == match.court_id).first() if match.court_id else None return { "id": match.id, "stage_id": match.stage_id, "court_id": match.court_id, "court_name": court.name if court else None, "round_number": match.round_number, "match_number": match.match_number, "player1_id": match.player1_id, "player2_id": match.player2_id, "player1_name": p1.name if p1 else None, "player2_name": p2.name if p2 else None, "team1_players": match.team1_players, "team2_players": match.team2_players, "score1": match.score1, "score2": match.score2, "winner_id": match.winner_id, "winner_team": match.winner_team, "status": match.status, "bracket_position": match.bracket_position, "next_winner_match_id": match.next_winner_match_id, "next_loser_match_id": match.next_loser_match_id, "started_at": match.started_at, "completed_at": match.completed_at, } @router.get("/{match_id}") def get_match(match_id: int, db: Session = Depends(get_db)): match = db.query(Match).filter(Match.id == match_id).first() if not match: raise HTTPException(status_code=404, detail="Match not found") return _match_detail(match, db) @router.post("/{match_id}/start") def start_match(match_id: int, db: Session = Depends(get_db)): match = db.query(Match).filter(Match.id == match_id).first() if not match: raise HTTPException(status_code=404, detail="Match not found") match.status = "in_progress" match.started_at = datetime.utcnow() # Auto-assign available court if none assigned if not match.court_id: stage = db.query(Stage).filter(Stage.id == match.stage_id).first() if stage: # Find available court in_use_court_ids = db.query(Match.court_id).filter( Match.status == "in_progress", Match.court_id.isnot(None) ).all() in_use_ids = [r[0] for r in in_use_court_ids] available_court = db.query(Court).filter( Court.status.in_(["available", "reserved"]), Court.id.notin_(in_use_ids) ).first() if available_court: match.court_id = available_court.id available_court.status = "in_use" db.commit() return _match_detail(match, db) @router.post("/{match_id}/score") def enter_score(match_id: int, data: MatchScoreUpdate, db: Session = Depends(get_db)): match = db.query(Match).filter(Match.id == match_id).first() if not match: raise HTTPException(status_code=404, detail="Match not found") match.score1 = data.score1 match.score2 = data.score2 match.status = "completed" match.completed_at = datetime.utcnow() # Determine winner if data.score1 > data.score2: match.winner_id = match.player1_id match.winner_team = 1 winner_id = match.player1_id loser_id = match.player2_id else: match.winner_id = match.player2_id match.winner_team = 2 winner_id = match.player2_id loser_id = match.player1_id # Free up court if match.court_id: court = db.query(Court).filter(Court.id == match.court_id).first() if court: court.status = "available" # Update player stats if winner_id: winner = db.query(Player).filter(Player.id == winner_id).first() if winner: winner.wins += 1 winner.matches_played += 1 winner.elo += 20 # Simple ELO gain if loser_id: loser = db.query(Player).filter(Player.id == loser_id).first() if loser: loser.losses += 1 loser.matches_played += 1 loser.elo = max(800, loser.elo - 15) # Advance bracket: place winner into next match if match.next_winner_match_id: next_match = db.query(Match).filter(Match.id == match.next_winner_match_id).first() if next_match: if next_match.player1_id is None: next_match.player1_id = winner_id elif next_match.player2_id is None: next_match.player2_id = winner_id db.commit() return _match_detail(match, db) @router.patch("/{match_id}/court") def assign_court(match_id: int, data: MatchCourtAssign, db: Session = Depends(get_db)): match = db.query(Match).filter(Match.id == match_id).first() if not match: raise HTTPException(status_code=404, detail="Match not found") # Free old court if match.court_id: old_court = db.query(Court).filter(Court.id == match.court_id).first() if old_court and old_court.status == "in_use": old_court.status = "available" match.court_id = data.court_id # Mark new court in use if match is in progress if data.court_id and match.status == "in_progress": new_court = db.query(Court).filter(Court.id == data.court_id).first() if new_court: new_court.status = "in_use" db.commit() return {"ok": True}