using CalledIt.DataAccess;
using CalledIt.Domain.Dtos;
using Microsoft.EntityFrameworkCore;

namespace CalledIt.Domain.Services;

public class LeaderboardService : ILeaderboardService
{
    private readonly CalledItDbContext _dbContext;

    public LeaderboardService(CalledItDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<LeaderboardResultDto> GetByCompetitionAsync(int competitionId, int userId)
    {
        var rows = await _dbContext.Database
            .SqlQuery<LeaderboardRow>($"""
                SELECT * FROM (
                    SELECT "UserId", "Username", "TotalPoints", "BetsPlaced", "BetsEvaluated",
                           CAST(ROW_NUMBER() OVER (ORDER BY "TotalPoints" DESC, "Username" ASC) AS int) AS "Rank"
                    FROM "UserCompetitionStats"
                    WHERE "CompetitionId" = {competitionId}
                ) ranked
                WHERE "Rank" <= 100 OR "UserId" = {userId}
                ORDER BY "Rank"
                """)
            .ToListAsync();

        var entries = rows
            .Take(100)
            .Select(r => new LeaderboardEntryDto(r.Rank, r.Username, r.TotalPoints, r.BetsPlaced, r.BetsEvaluated))
            .ToList();

        var userRow = rows.FirstOrDefault(r => r.UserId == userId);
        var userInTop100 = userRow is not null && userRow.Rank <= 100;

        var currentUserEntry = userRow is not null && !userInTop100
            ? new LeaderboardEntryDto(userRow.Rank, userRow.Username, userRow.TotalPoints, userRow.BetsPlaced, userRow.BetsEvaluated)
            : null;

        return new LeaderboardResultDto(entries, currentUserEntry);
    }

#pragma warning disable CA1812 // instantiated by EF Core via reflection
    private sealed record LeaderboardRow(int UserId, string Username, int TotalPoints, int BetsPlaced, int BetsEvaluated, int Rank);
#pragma warning restore CA1812
}
