using MediatR;
using MiningService.Domain.AggregatesModel.MinerAggregate;

namespace MiningService.API.Application.Queries;

/// <summary>
/// EN: Query to get mining history for a user.
/// VI: Query để lấy lịch sử đào của người dùng.
/// </summary>
public record GetMiningHistoryQuery(Guid UserId, int Page = 1, int PageSize = 20) : IRequest<MiningHistoryDto>;

public record MiningHistoryDto(
    List<MiningHistoryItemDto> Items,
    int TotalCount,
    int Page,
    int PageSize);

public record MiningHistoryItemDto(
    Guid Id,
    decimal Amount,
    string Source,
    decimal RateSnapshot,
    DateTime CreatedAt);

public class GetMiningHistoryQueryHandler : IRequestHandler<GetMiningHistoryQuery, MiningHistoryDto>
{
    private readonly IMinerRepository _minerRepository;

    public GetMiningHistoryQueryHandler(IMinerRepository minerRepository)
    {
        _minerRepository = minerRepository;
    }

    public async Task<MiningHistoryDto> Handle(GetMiningHistoryQuery request, CancellationToken cancellationToken)
    {
        var miner = await _minerRepository.GetByUserIdAsync(request.UserId, cancellationToken);
        if (miner == null)
            return new MiningHistoryDto(new List<MiningHistoryItemDto>(), 0, request.Page, request.PageSize);

        var history = miner.MiningHistories
            .OrderByDescending(h => h.EarnedAt)
            .Skip((request.Page - 1) * request.PageSize)
            .Take(request.PageSize)
            .Select(h => new MiningHistoryItemDto(h.Id, h.PointsEarned, h.Source, h.HourlyRateSnapshot, h.EarnedAt))
            .ToList();

        return new MiningHistoryDto(history, miner.MiningHistories.Count, request.Page, request.PageSize);
    }
}

/// <summary>
/// EN: Query to get current mining rate breakdown.
/// VI: Query để lấy chi tiết tỷ lệ đào hiện tại.
/// </summary>
public record GetMiningRateQuery(Guid UserId) : IRequest<MiningRateDto?>;

public record MiningRateDto(
    decimal BaseRate,
    decimal RoleMultiplier,
    decimal CircleBonus,
    decimal ReferralBonus,
    decimal StreakBonus,
    decimal TotalRate,
    decimal HourlyPoints,
    decimal DailyPoints);

public class GetMiningRateQueryHandler : IRequestHandler<GetMiningRateQuery, MiningRateDto?>
{
    private readonly IMinerRepository _minerRepository;

    public GetMiningRateQueryHandler(IMinerRepository minerRepository)
    {
        _minerRepository = minerRepository;
    }

    public async Task<MiningRateDto?> Handle(GetMiningRateQuery request, CancellationToken cancellationToken)
    {
        var miner = await _minerRepository.GetByUserIdAsync(request.UserId, cancellationToken);
        if (miner == null) return null;

        var rate = miner.CurrentRate;
        var baseRate = 0.25m;
        var roleMultiplier = GetRoleMultiplier(miner.Role);
        var circleBonus = miner.CircleId.HasValue ? 0.25m : 0m;
        var referralBonus = 0m; // Would need to query repository for actual count
        var streakBonus = miner.Streak.BonusMultiplier;
        
        var totalRate = baseRate * (1 + roleMultiplier) * (1 + circleBonus) * (1 + referralBonus) * (1 + streakBonus);
        
        return new MiningRateDto(
            baseRate,
            roleMultiplier,
            circleBonus,
            referralBonus,
            streakBonus,
            totalRate,
            totalRate,
            totalRate * 24);
    }

    private static decimal GetRoleMultiplier(MinerRole role) => role switch
    {
        MinerRole.Pioneer => 0m,
        MinerRole.Contributor => 0.10m,
        MinerRole.Ambassador => 0.25m,
        MinerRole.NodeOperator => 0.50m,
        _ => 0m
    };
}

/// <summary>
/// EN: Query to get top miners leaderboard.
/// VI: Query để lấy bảng xếp hạng thợ đào.
/// </summary>
public record GetLeaderboardQuery(int Limit = 100) : IRequest<LeaderboardDto>;

public record LeaderboardDto(List<LeaderboardEntryDto> Entries);

public record LeaderboardEntryDto(
    int Rank,
    Guid MinerId,
    Guid UserId,
    decimal TotalPoints,
    int StreakDays,
    MinerRole Role);

public class GetLeaderboardQueryHandler : IRequestHandler<GetLeaderboardQuery, LeaderboardDto>
{
    private readonly IMinerRepository _minerRepository;

    public GetLeaderboardQueryHandler(IMinerRepository minerRepository)
    {
        _minerRepository = minerRepository;
    }

    public async Task<LeaderboardDto> Handle(GetLeaderboardQuery request, CancellationToken cancellationToken)
    {
        var topMiners = await _minerRepository.GetTopMinersAsync(request.Limit, cancellationToken);
        
        var entries = topMiners.Select((m, i) => new LeaderboardEntryDto(
            i + 1,
            m.Id,
            m.UserId,
            m.TotalMinedPoints,
            m.Streak.CurrentStreak,
            m.Role)).ToList();

        return new LeaderboardDto(entries);
    }
}
