# backend/app/routes/tournaments/report.py
from typing import Optional

from fastapi import Depends, HTTPException, Query
from sqlalchemy.orm import Session
from sqlalchemy.orm.attributes import flag_modified

from ... import crud, logic, models, schemas
from ...constants import SUCCESS, MatchStatus
from ...core.auth import get_optional_user
from ...core.websocket_manager import send_ws_update
from ...database import get_db
from . import router


def _check_auth(t: models.Tournament, user: Optional[str], code: Optional[str]):
    is_admin = user is not None
    code_matches = code is not None and str(code).strip() == str(t.code).strip()

    if not is_admin and not code_matches:
        raise HTTPException(403, "Invalid tournament code or admin privileges required")
@router.post("/{id}/matches/{match_id}/score")
async def report_score(
    id: str,
    match_id: str,
    report: schemas.ScoreReport,
    db: Session = Depends(get_db),
    user: Optional[str] = Depends(get_optional_user),
):
    t = crud.get_tournament(db, id, lock=True) 
    if not t:
        raise HTTPException(404, "Tournament not found")

    _check_auth(t, user, report.code)

    match = crud.get_match(db, match_id)
    if not match:
        raise HTTPException(404, "Match not found")

    if not report.sets:
        raise HTTPException(400, "No sets submitted")

    if match.winner_team_id:
        logic.undo_advancement(db, match)

    _apply_score(match, report.sets)
    flag_modified(match, "sets")

    if match.winner_team_id:
        logic.advance_winner(db, match, match.winner_team_id)

    db.commit()

    await send_ws_update(id)
    return SUCCESS


@router.patch("/{id}/matches/{match_id}/score")
async def edit_score(
    id: str,
    match_id: str,
    report: schemas.ScoreReport,
    db: Session = Depends(get_db),
    user: Optional[str] = Depends(get_optional_user),
):
    t = crud.get_tournament(db, id, lock=True)
    if not t:
        raise HTTPException(404, "Tournament not found")

    _check_auth(t, user, report.code)

    match = crud.get_match(db, match_id)
    if not match:
        raise HTTPException(404, "Match not found")

    logic.undo_advancement(db, match)

    if report.sets:
        _apply_score(match, report.sets)

    flag_modified(match, "sets")

    if match.winner_team_id:
        logic.advance_winner(db, match, match.winner_team_id)

    db.commit()

    await send_ws_update(id)
    return SUCCESS


@router.delete("/{id}/matches/{match_id}/score")
async def clear_score(
    id: str,
    match_id: str,
    code: Optional[str] = Query(None),
    db: Session = Depends(get_db),
    user: Optional[str] = Depends(get_optional_user),
):
    t = crud.get_tournament(db, id, lock=True)
    if not t:
        raise HTTPException(404, "Tournament not found")

    _check_auth(t, user, code)

    match = crud.get_match(db, match_id)
    if not match:
        raise HTTPException(404, "Match not found")

    logic.undo_advancement(db, match)

    match.winner_team_id = None
    if match.p1_team_id and match.p2_team_id:
        match.status = MatchStatus.PENDING
    else:
        match.status = MatchStatus.SCHEDULED
    match.sets = []

    flag_modified(match, "sets")
    db.commit()

    await send_ws_update(id)
    return SUCCESS


def _apply_score(match: models.Match, sets: list[schemas.SetScore]):
    """
    Calculates winner based on sets and updates the match object.
    Does NOT commit to DB.
    """
    p1_wins = sum(1 for s in sets if s.p1 > s.p2)
    p2_wins = sum(1 for s in sets if s.p2 > s.p1)

    if p1_wins > p2_wins:
        match.winner_team_id = match.p1_team_id
    elif p2_wins > p1_wins:
        match.winner_team_id = match.p2_team_id
    else:
        p1_points = sum(s.p1 for s in sets)
        p2_points = sum(s.p2 for s in sets)

        if p1_points == p2_points:
            raise HTTPException(400, "Absolute tie: Sets and Points are equal.")

        match.winner_team_id = (
            match.p1_team_id if p1_points > p2_points else match.p2_team_id
        )

    match.status = MatchStatus.FINISHED
    match.sets = [s.model_dump() for s in sets]
