using MediatR;
using MissionService.Domain.AggregatesModel.MissionAggregate;
using MissionService.Domain.AggregatesModel.TaskAggregate;
using TaskStatus = MissionService.Domain.AggregatesModel.TaskAggregate.TaskStatus;

namespace MissionService.API.Application.Queries;

/// <summary>
/// EN: Handler for GetAvailableMissionsQuery
/// VI: Handler cho GetAvailableMissionsQuery
/// </summary>
public class GetAvailableMissionsQueryHandler : IRequestHandler<GetAvailableMissionsQuery, MissionsListResult>
{
    private readonly IMissionRepository _missionRepository;
    private readonly IUserTaskRepository _taskRepository;

    public GetAvailableMissionsQueryHandler(
        IMissionRepository missionRepository,
        IUserTaskRepository taskRepository)
    {
        _missionRepository = missionRepository;
        _taskRepository = taskRepository;
    }

    public async Task<MissionsListResult> Handle(GetAvailableMissionsQuery request, CancellationToken cancellationToken)
    {
        var missions = await _missionRepository.GetActiveMissionsAsync(cancellationToken);
        var userTasks = await _taskRepository.GetByUserIdAsync(request.UserId, cancellationToken);

        var summaries = missions.Select(m => MapToSummary(m, userTasks)).ToList();
        return new MissionsListResult(summaries);
    }

    private static MissionSummaryDto MapToSummary(Mission mission, IReadOnlyList<UserTask> userTasks)
    {
        var userTask = userTasks.FirstOrDefault(t => t.MissionId == mission.Id && 
            (t.Status == TaskStatus.InProgress || t.Status == TaskStatus.PendingVerification));

        return new MissionSummaryDto(
            Id: mission.Id,
            Code: mission.Code,
            Title: mission.TitleEn,
            Description: mission.DescriptionEn,
            Type: mission.Type.Name,
            Category: mission.Category.Name,
            RewardPoints: mission.Reward.Points,
            Frequency: mission.Frequency.Name,
            MaxCompletions: mission.MaxCompletions,
            IsAvailable: mission.IsAvailable(),
            UserProgress: userTask != null ? MapToProgress(userTask) : null);
    }

    private static UserTaskProgressDto MapToProgress(UserTask task)
    {
        return new UserTaskProgressDto(
            TaskId: task.Id,
            CurrentValue: task.Progress.CurrentValue,
            TargetValue: task.Progress.TargetValue,
            PercentComplete: task.Progress.PercentComplete,
            Status: task.Status.Name,
            StartedAt: task.StartedAt,
            CompletedAt: task.CompletedAt,
            RewardClaimed: task.RewardClaimed);
    }
}

/// <summary>
/// EN: Handler for GetMissionDetailsQuery
/// VI: Handler cho GetMissionDetailsQuery
/// </summary>
public class GetMissionDetailsQueryHandler : IRequestHandler<GetMissionDetailsQuery, MissionDetailsResult?>
{
    private readonly IMissionRepository _missionRepository;
    private readonly IUserTaskRepository _taskRepository;

    public GetMissionDetailsQueryHandler(
        IMissionRepository missionRepository,
        IUserTaskRepository taskRepository)
    {
        _missionRepository = missionRepository;
        _taskRepository = taskRepository;
    }

    public async Task<MissionDetailsResult?> Handle(GetMissionDetailsQuery request, CancellationToken cancellationToken)
    {
        var mission = await _missionRepository.GetByIdAsync(request.MissionId, cancellationToken);
        if (mission == null) return null;

        UserTaskProgressDto? userProgress = null;
        if (request.UserId.HasValue)
        {
            var userTask = await _taskRepository.GetByUserAndMissionAsync(
                request.UserId.Value, request.MissionId, cancellationToken);
            if (userTask != null)
            {
                userProgress = new UserTaskProgressDto(
                    TaskId: userTask.Id,
                    CurrentValue: userTask.Progress.CurrentValue,
                    TargetValue: userTask.Progress.TargetValue,
                    PercentComplete: userTask.Progress.PercentComplete,
                    Status: userTask.Status.Name,
                    StartedAt: userTask.StartedAt,
                    CompletedAt: userTask.CompletedAt,
                    RewardClaimed: userTask.RewardClaimed);
            }
        }

        return new MissionDetailsResult(
            Id: mission.Id,
            Code: mission.Code,
            TitleEn: mission.TitleEn,
            TitleVi: mission.TitleVi,
            DescriptionEn: mission.DescriptionEn,
            DescriptionVi: mission.DescriptionVi,
            Type: mission.Type.Name,
            Category: mission.Category.Name,
            Reward: new MissionRewardDto(
                mission.Reward.Points,
                mission.Reward.MiningBoostPercent,
                mission.Reward.ExperiencePoints,
                mission.Reward.BadgeId),
            Frequency: mission.Frequency.Name,
            MaxCompletions: mission.MaxCompletions,
            StartDate: mission.StartDate,
            EndDate: mission.EndDate,
            Status: mission.Status.Name,
            Rules: mission.Rules.Select(r => new MissionRuleDto(r.RuleType, r.Operator, r.Value)).ToList(),
            UserProgress: userProgress);
    }
}

/// <summary>
/// EN: Handler for GetMissionsByCategoryQuery
/// VI: Handler cho GetMissionsByCategoryQuery
/// </summary>
public class GetMissionsByCategoryQueryHandler : IRequestHandler<GetMissionsByCategoryQuery, MissionsListResult>
{
    private readonly IMissionRepository _missionRepository;

    public GetMissionsByCategoryQueryHandler(IMissionRepository missionRepository)
    {
        _missionRepository = missionRepository;
    }

    public async Task<MissionsListResult> Handle(GetMissionsByCategoryQuery request, CancellationToken cancellationToken)
    {
        var category = MissionCategory.FromDisplayName<MissionCategory>(request.Category);
        var missions = await _missionRepository.GetByCategoryAsync(category, cancellationToken);

        var summaries = missions.Select(m => new MissionSummaryDto(
            Id: m.Id,
            Code: m.Code,
            Title: m.TitleEn,
            Description: m.DescriptionEn,
            Type: m.Type.Name,
            Category: m.Category.Name,
            RewardPoints: m.Reward.Points,
            Frequency: m.Frequency.Name,
            MaxCompletions: m.MaxCompletions,
            IsAvailable: m.IsAvailable(),
            UserProgress: null)).ToList();

        return new MissionsListResult(summaries);
    }
}

/// <summary>
/// EN: Handler for GetUserMissionProgressQuery - returns user's overall mission progress.
/// VI: Handler cho GetUserMissionProgressQuery - trả về tiến độ tổng thể của user trên các mission.
/// </summary>
public class GetUserMissionProgressQueryHandler : IRequestHandler<GetUserMissionProgressQuery, UserMissionProgressResult>
{
    private readonly IMissionRepository _missionRepository;
    private readonly IUserTaskRepository _taskRepository;

    public GetUserMissionProgressQueryHandler(
        IMissionRepository missionRepository,
        IUserTaskRepository taskRepository)
    {
        _missionRepository = missionRepository;
        _taskRepository = taskRepository;
    }

    public async Task<UserMissionProgressResult> Handle(GetUserMissionProgressQuery request, CancellationToken cancellationToken)
    {
        // EN: Get all active missions and user's tasks / VI: Lấy tất cả missions đang hoạt động và tasks của user
        var missions = await _missionRepository.GetActiveMissionsAsync(cancellationToken);
        var userTasks = await _taskRepository.GetByUserIdAsync(request.UserId, cancellationToken);

        // EN: Separate active and completed tasks / VI: Phân loại tasks đang hoạt động và đã hoàn thành
        var activeTasks = userTasks
            .Where(t => t.Status == TaskStatus.InProgress || t.Status == TaskStatus.PendingVerification)
            .ToList();

        var completedTasks = userTasks
            .Where(t => t.Status == TaskStatus.Completed)
            .ToList();

        // EN: Calculate total points earned from claimed rewards
        // VI: Tính tổng điểm đã nhận từ phần thưởng đã claim
        var completedMissionIds = completedTasks.Select(t => t.MissionId).ToHashSet();
        var totalPointsEarned = 0m;
        foreach (var mission in missions.Where(m => completedMissionIds.Contains(m.Id)))
        {
            var taskCount = completedTasks.Count(t => t.MissionId == mission.Id && t.RewardClaimed);
            totalPointsEarned += mission.Reward.Points * taskCount;
        }

        // EN: Map active missions with progress / VI: Map các mission đang hoạt động với tiến độ
        var activeMissionSummaries = missions
            .Where(m => activeTasks.Any(t => t.MissionId == m.Id))
            .Select(m =>
            {
                var task = activeTasks.First(t => t.MissionId == m.Id);
                return new MissionSummaryDto(
                    Id: m.Id,
                    Code: m.Code,
                    Title: m.TitleEn,
                    Description: m.DescriptionEn,
                    Type: m.Type.Name,
                    Category: m.Category.Name,
                    RewardPoints: m.Reward.Points,
                    Frequency: m.Frequency.Name,
                    MaxCompletions: m.MaxCompletions,
                    IsAvailable: m.IsAvailable(),
                    UserProgress: new UserTaskProgressDto(
                        TaskId: task.Id,
                        CurrentValue: task.Progress.CurrentValue,
                        TargetValue: task.Progress.TargetValue,
                        PercentComplete: task.Progress.PercentComplete,
                        Status: task.Status.Name,
                        StartedAt: task.StartedAt,
                        CompletedAt: task.CompletedAt,
                        RewardClaimed: task.RewardClaimed));
            })
            .ToList();

        // EN: Map completed missions / VI: Map các mission đã hoàn thành
        var completedMissionSummaries = missions
            .Where(m => completedMissionIds.Contains(m.Id))
            .Select(m =>
            {
                var task = completedTasks.First(t => t.MissionId == m.Id);
                return new MissionSummaryDto(
                    Id: m.Id,
                    Code: m.Code,
                    Title: m.TitleEn,
                    Description: m.DescriptionEn,
                    Type: m.Type.Name,
                    Category: m.Category.Name,
                    RewardPoints: m.Reward.Points,
                    Frequency: m.Frequency.Name,
                    MaxCompletions: m.MaxCompletions,
                    IsAvailable: m.IsAvailable(),
                    UserProgress: new UserTaskProgressDto(
                        TaskId: task.Id,
                        CurrentValue: task.Progress.CurrentValue,
                        TargetValue: task.Progress.TargetValue,
                        PercentComplete: task.Progress.PercentComplete,
                        Status: task.Status.Name,
                        StartedAt: task.StartedAt,
                        CompletedAt: task.CompletedAt,
                        RewardClaimed: task.RewardClaimed));
            })
            .ToList();

        return new UserMissionProgressResult(
            TotalMissions: missions.Count,
            CompletedMissionsCount: completedMissionIds.Count,
            InProgressMissions: activeTasks.Count,
            TotalPointsEarned: totalPointsEarned,
            ActiveMissions: activeMissionSummaries,
            CompletedMissions: completedMissionSummaries);
    }
}
