using Application.Abstractions.Data;
using Application.Abstractions.Messaging;
using Microsoft.EntityFrameworkCore;

namespace Application.Features.Api.Attendance.GetList;

internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Response>
{
    public async Task<Response> Handle(Query request, CancellationToken ct)
    {
        // 로컬(서버 KST) 기준 날짜를 UTC 범위로 환산
        var targetDateLocal = DateTime.SpecifyKind(request.Date?.Date ?? DateTime.Now.Date, DateTimeKind.Local);
        var startUtc = targetDateLocal.ToUniversalTime();
        var endUtc = startUtc.AddDays(1);

        var query = db.Attendance.AsNoTracking()
            .Where(x => x.CreatedAt >= startUtc && x.CreatedAt < endUtc);

        var total = await query.CountAsync(ct);

        // 시간순 Rank 사전 계산 (정렬 옵션과 무관하게 항상 시간 기준)
        var rankList = await query
            .OrderBy(x => x.CreatedAt)
            .Select(x => new { x.MemberID, x.CreatedAt })
            .ToListAsync(ct);

        var rankMap = new Dictionary<int, int>();
        for (var i = 0; i < rankList.Count; i++)
        {
            rankMap[rankList[i].MemberID] = i + 1;
        }

        // 정렬 분기
        var sortBy = request.SortBy ?? "time";
        IQueryable<Domain.Entities.Members.Attendance> sortedQuery = sortBy switch
        {
            "count" => from a in query
                       join stats in db.MemberStats.AsNoTracking() on a.MemberID equals stats.MemberID
                       orderby stats.AttendanceCount descending, a.CreatedAt
                       select a,
            "streak" => query.OrderByDescending(x => x.ConsecutiveDays).ThenBy(x => x.CreatedAt),
            _ => query.OrderBy(x => x.CreatedAt)
        };

        var list = await sortedQuery
            .Skip((request.Page - 1) * request.PerPage)
            .Take(request.PerPage)
            .Select(x => new AttendanceItem(
                0,
                x.MemberID,
                x.Member.SID,
                x.Member.Name ?? "",
                x.Member.Thumb,
                x.Greeting,
                x.ConsecutiveDays,
                x.ExpRewarded,
                x.PointRewarded,
                x.CreatedAt
            ))
            .ToListAsync(ct);

        // Rank 주입 + UTC → 로컬 변환
        var ranked = list.Select(item => item with {
            Rank = rankMap.GetValueOrDefault(item.MemberID),
            CreatedAt = DateTime.SpecifyKind(item.CreatedAt, DateTimeKind.Utc).ToLocalTime()
        }).ToList();

        return new Response(total, ranked);
    }
}
