from typing import TYPE_CHECKING, Annotated
from uuid import UUID

from fastapi import APIRouter, Depends, status
from sqlalchemy import func, select
from sqlalchemy.orm import Session, selectinload

from yak_server.database.models import GroupModel, Role, UserKnockoutGuessModel, UserModel
from yak_server.helpers.database import get_db
from yak_server.helpers.language import DEFAULT_LANGUAGE, Lang
from yak_server.v1.helpers.auth import require_user
from yak_server.v1.models.generic import ErrorOut, GenericOut, ValidationErrorOut
from yak_server.v1.models.groups import GroupOut
from yak_server.v1.models.results import ScoreBoardResponse, ScoreBoardUserResult, UserResult

if TYPE_CHECKING:
    from collections.abc import Sequence


router = APIRouter(tags=["results"])


@router.get(
    "/score_board",
    responses={
        status.HTTP_401_UNAUTHORIZED: {"model": ErrorOut},
        status.HTTP_422_UNPROCESSABLE_CONTENT: {"model": ValidationErrorOut},
    },
)
def retrieve_score_board(
    _: Annotated[UserModel, Depends(require_user)],
    db: Annotated[Session, Depends(get_db)],
    lang: Lang = DEFAULT_LANGUAGE,
) -> GenericOut[ScoreBoardResponse]:
    knockout_groups = (
        db
        .query(GroupModel)
        .where(GroupModel.id.in_(select(UserKnockoutGuessModel.group_id).distinct()))
        .order_by(GroupModel.index)
        .all()
    )

    users = list(
        db
        .query(UserModel)
        .options(
            selectinload(UserModel.knockout_guesses).selectinload(UserKnockoutGuessModel.group)
        )
        .order_by(UserModel.points.desc())
        .where(UserModel.role != Role.ADMIN)
    )

    return GenericOut(
        result=ScoreBoardResponse(
            groups=[GroupOut.from_instance(g, lang=lang) for g in knockout_groups],
            results=[
                ScoreBoardUserResult.from_instance(user, rank=rank)
                for rank, user in enumerate(users, 1)
            ],
        )
    )


def compute_rank(db: Session, user_id: UUID) -> int:
    subq = (
        db
        .query(
            UserModel,
            func.row_number().over(order_by=UserModel.points.desc()).label("rownum"),
        )
        .where(UserModel.role != Role.ADMIN)
        .subquery()
    )

    rank: Sequence[int] = db.query(subq.c.rownum).where(subq.c.id == user_id).first()

    if not rank:
        return 0

    return rank[0]


@router.get(
    "/results",
    responses={
        status.HTTP_401_UNAUTHORIZED: {"model": ErrorOut},
        status.HTTP_422_UNPROCESSABLE_CONTENT: {"model": ValidationErrorOut},
    },
)
def retrieve_user_results(
    user: Annotated[UserModel, Depends(require_user)],
    db: Annotated[Session, Depends(get_db)],
    lang: Lang = DEFAULT_LANGUAGE,
) -> GenericOut[UserResult]:
    rank = compute_rank(db, user.id)

    user_with_guesses = (
        db
        .query(UserModel)
        .options(
            selectinload(UserModel.knockout_guesses).selectinload(UserKnockoutGuessModel.group)
        )
        .filter_by(id=user.id)
        .one()
    )

    return GenericOut(result=UserResult.from_instance(user_with_guesses, rank=rank, lang=lang))
