using MediatR;
using MissionService.Domain.AggregatesModel.CheckInAggregate;

namespace MissionService.API.Application.Queries;

/// <summary>
/// EN: Handler for GetCheckInStatusQuery
/// VI: Handler cho GetCheckInStatusQuery
/// </summary>
public class GetCheckInStatusQueryHandler : IRequestHandler<GetCheckInStatusQuery, CheckInStatusResult>
{
    private readonly IUserCheckInRepository _checkInRepository;

    public GetCheckInStatusQueryHandler(IUserCheckInRepository checkInRepository)
    {
        _checkInRepository = checkInRepository;
    }

    public async Task<CheckInStatusResult> Handle(GetCheckInStatusQuery request, CancellationToken cancellationToken)
    {
        var checkIn = await _checkInRepository.GetByUserIdAsync(request.UserId, cancellationToken);

        if (checkIn == null)
        {
            return new CheckInStatusResult(
                CurrentStreak: 0,
                LongestStreak: 0,
                TotalCheckIns: 0,
                LastCheckInDate: null,
                CanCheckInToday: true,
                NextReward: GetNextRewardPreview(1));
        }

        return new CheckInStatusResult(
            CurrentStreak: checkIn.CurrentStreak,
            LongestStreak: checkIn.LongestStreak,
            TotalCheckIns: checkIn.TotalCheckIns,
            LastCheckInDate: checkIn.LastCheckInDate,
            CanCheckInToday: checkIn.CanCheckInToday(),
            NextReward: GetNextRewardPreview(checkIn.CurrentStreak + 1));
    }

    private static StreakRewardPreview GetNextRewardPreview(int nextDay)
    {
        // EN: Calculate next milestone based on streak day
        // VI: Tính mốc thưởng tiếp theo dựa trên ngày streak
        var milestones = new[] { 7, 14, 21, 30 };
        var nextMilestone = milestones.FirstOrDefault(m => m >= nextDay);
        if (nextMilestone == 0) nextMilestone = 30;

        var config = StreakBonusConfig.Default();
        var (dailyPoints, _, milestoneBonus, _) = config.CalculateReward(nextDay);

        return new StreakRewardPreview(
            NextMilestone: nextMilestone,
            DaysToMilestone: Math.Max(0, nextMilestone - nextDay + 1),
            DailyPoints: dailyPoints,
            MilestoneBonus: milestoneBonus);
    }
}

/// <summary>
/// EN: Handler for GetCheckInHistoryQuery
/// VI: Handler cho GetCheckInHistoryQuery
/// </summary>
public class GetCheckInHistoryQueryHandler : IRequestHandler<GetCheckInHistoryQuery, CheckInHistoryResult>
{
    private readonly IUserCheckInRepository _checkInRepository;

    public GetCheckInHistoryQueryHandler(IUserCheckInRepository checkInRepository)
    {
        _checkInRepository = checkInRepository;
    }

    public async Task<CheckInHistoryResult> Handle(GetCheckInHistoryQuery request, CancellationToken cancellationToken)
    {
        var checkIn = await _checkInRepository.GetByUserIdAsync(request.UserId, cancellationToken);

        if (checkIn == null)
        {
            return new CheckInHistoryResult(request.Year, request.Month, Array.Empty<CheckInDayDto>());
        }

        var days = checkIn.GetMonthlyCheckIns(request.Year, request.Month)
            .Select(d => new CheckInDayDto(d.Date, d.PointsEarned, d.IsMilestone, d.StreakOnDay))
            .ToList();

        return new CheckInHistoryResult(request.Year, request.Month, days);
    }
}

/// <summary>
/// EN: Handler for GetCheckInLeaderboardQuery
/// VI: Handler cho GetCheckInLeaderboardQuery
/// </summary>
public class GetCheckInLeaderboardQueryHandler : IRequestHandler<GetCheckInLeaderboardQuery, CheckInLeaderboardResult>
{
    private readonly IUserCheckInRepository _checkInRepository;

    public GetCheckInLeaderboardQueryHandler(IUserCheckInRepository checkInRepository)
    {
        _checkInRepository = checkInRepository;
    }

    public async Task<CheckInLeaderboardResult> Handle(GetCheckInLeaderboardQuery request, CancellationToken cancellationToken)
    {
        var topStreaks = await _checkInRepository.GetTopStreaksAsync(request.Count, cancellationToken);

        var entries = topStreaks.Select((c, index) => new LeaderboardEntry(
            Rank: index + 1,
            UserId: c.UserId,
            CurrentStreak: c.CurrentStreak,
            LongestStreak: c.LongestStreak,
            TotalCheckIns: c.TotalCheckIns
        )).ToList();

        return new CheckInLeaderboardResult(entries);
    }
}
