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

namespace Application.Features.Api.Attendance.GetCalendar;

internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Response>
{
    public async Task<Response> Handle(Query request, CancellationToken ct)
    {
        // 로컬(서버 KST) 기준 월 범위를 UTC 로 환산
        var monthStartLocal = new DateTime(request.Year, request.Month, 1, 0, 0, 0, DateTimeKind.Local);
        var startUtc = monthStartLocal.ToUniversalTime();
        var endUtc = monthStartLocal.AddMonths(1).ToUniversalTime();

        // 해당 월 출석 레코드 조회
        var records = await db.Attendance.AsNoTracking()
            .Where(x => x.CreatedAt >= startUtc && x.CreatedAt < endUtc)
            .Select(x => new { x.MemberID, x.CreatedAt, x.ConsecutiveDays })
            .ToListAsync(ct);

        // EF Core 는 Unspecified 로 반환하므로 UTC 명시 후 로컬 변환
        var localRecords = records.Select(r => new {
            r.MemberID,
            CreatedAt = DateTime.SpecifyKind(r.CreatedAt, DateTimeKind.Utc).ToLocalTime(),
            r.ConsecutiveDays
        }).ToList();

        // KST 달력일(yyyy-MM-dd) 문자열 기준으로 집계 (UTC 직렬화에 의한 하루 밀림 방지)
        var dailyCounts = localRecords
            .GroupBy(x => x.CreatedAt.ToString("yyyy-MM-dd"))
            .ToDictionary(g => g.Key, g => g.Count());

        var myRecords = request.MemberID is > 0
            ? localRecords.Where(x => x.MemberID == request.MemberID.Value).OrderBy(x => x.CreatedAt).ToList()
            : [];

        var daysInMonth = DateTime.DaysInMonth(request.Year, request.Month);
        var days = new List<CalendarDay>(daysInMonth);

        for (var day = 1; day <= daysInMonth; day++)
        {
            var dateStr = $"{request.Year:D4}-{request.Month:D2}-{day:D2}";
            var myRecord = myRecords.FirstOrDefault(r => r.CreatedAt.ToString("yyyy-MM-dd") == dateStr);
            var totalCount = dailyCounts.GetValueOrDefault(dateStr);

            days.Add(new CalendarDay(
                dateStr,
                myRecord?.CreatedAt,
                myRecord?.ConsecutiveDays ?? 0,
                totalCount
            ));
        }

        return new Response(days);
    }
}
