using MediatR;
using Microsoft.EntityFrameworkCore;
using PromotionService.API.Application.DTOs;
using PromotionService.Domain.AggregatesModel.CampaignAggregate;
using PromotionService.Domain.AggregatesModel.RedemptionAggregate;
using PromotionService.Infrastructure;

namespace PromotionService.API.Application.Queries;

/// <summary>
/// EN: Handler for GetAllCampaignsQuery.
/// VI: Handler cho GetAllCampaignsQuery.
/// </summary>
public class GetAllCampaignsQueryHandler : IRequestHandler<GetAllCampaignsQuery, PaginatedResponse<AdminCampaignListDto>>
{
    private readonly PromotionServiceContext _context;

    public GetAllCampaignsQueryHandler(PromotionServiceContext context)
    {
        _context = context;
    }

    public async Task<PaginatedResponse<AdminCampaignListDto>> Handle(GetAllCampaignsQuery request, CancellationToken cancellationToken)
    {
        var query = _context.Campaigns.AsQueryable();

        if (request.MerchantId.HasValue)
            query = query.Where(c => c.MerchantId == request.MerchantId.Value);

        if (!string.IsNullOrEmpty(request.Status))
        {
            var statusId = request.Status.ToLower() switch
            {
                "draft" => 1,
                "active" => 2,
                "paused" => 3,
                "completed" => 4,
                "cancelled" => 5,
                _ => 0
            };
            if (statusId > 0)
                query = query.Where(c => c.StatusId == statusId);
        }

        if (!string.IsNullOrEmpty(request.SearchTerm))
            query = query.Where(c => c.Name.Contains(request.SearchTerm));

        var totalCount = await query.CountAsync(cancellationToken);
        var totalPages = (int)Math.Ceiling(totalCount / (double)request.PageSize);

        var items = await query
            .OrderByDescending(c => c.CreatedAt)
            .Skip((request.PageNumber - 1) * request.PageSize)
            .Take(request.PageSize)
            .Select(c => new AdminCampaignListDto(
                c.Id,
                c.MerchantId,
                c.Name,
                c.BackingAssetTypeId == 1 ? "Currency" : "Point",
                c.BackingAssetCode,
                c.FaceValue,
                c.AcquisitionTypeId == 1 ? "Free" : c.AcquisitionTypeId == 2 ? "ExchangePoints" : "Purchase",
                c.StatusId == 1 ? "Draft" : c.StatusId == 2 ? "Active" : c.StatusId == 3 ? "Paused" : c.StatusId == 4 ? "Completed" : "Cancelled",
                c.TotalVouchers,
                c.IssuedVouchers,
                c.StartDate,
                c.EndDate,
                c.CreatedAt))
            .ToListAsync(cancellationToken);

        return new PaginatedResponse<AdminCampaignListDto>(items, totalCount, request.PageNumber, request.PageSize, totalPages);
    }
}

/// <summary>
/// EN: Handler for GetAdminCampaignStatisticsQuery.
/// VI: Handler cho GetAdminCampaignStatisticsQuery.
/// </summary>
public class GetAdminCampaignStatisticsQueryHandler : IRequestHandler<GetAdminCampaignStatisticsQuery, AdminCampaignStatisticsDto?>
{
    private readonly PromotionServiceContext _context;

    public GetAdminCampaignStatisticsQueryHandler(PromotionServiceContext context)
    {
        _context = context;
    }

    public async Task<AdminCampaignStatisticsDto?> Handle(GetAdminCampaignStatisticsQuery request, CancellationToken cancellationToken)
    {
        var campaign = await _context.Campaigns
            .Include(c => c.Vouchers)
            .FirstOrDefaultAsync(c => c.Id == request.CampaignId, cancellationToken);

        if (campaign == null) return null;

        var redemptions = await _context.Redemptions
            .Where(r => r.CampaignId == request.CampaignId)
            .ToListAsync(cancellationToken);

        var vouchers = campaign.Vouchers.ToList();
        var claimed = vouchers.Count(v => v.OwnerId.HasValue);
        var redeemed = vouchers.Count(v => v.StatusId == VoucherStatus.FullyRedeemed.Id || v.StatusId == VoucherStatus.PartiallyRedeemed.Id);
        var expired = vouchers.Count(v => v.StatusId == VoucherStatus.Expired.Id);
        var totalRedeemed = redemptions.Sum(r => r.AmountUsed);
        var totalRefunded = redemptions.Sum(r => r.AmountRefunded);

        return new AdminCampaignStatisticsDto(
            campaign.Id,
            campaign.Name,
            campaign.StatusId == 1 ? "Draft" : campaign.StatusId == 2 ? "Active" : campaign.StatusId == 3 ? "Paused" : campaign.StatusId == 4 ? "Completed" : "Cancelled",
            campaign.TotalVouchers,
            campaign.IssuedVouchers,
            campaign.AvailableVoucherCount,
            claimed,
            redeemed,
            expired,
            campaign.FaceValue * campaign.TotalVouchers,
            totalRedeemed,
            totalRefunded,
            campaign.TotalVouchers > 0 ? (decimal)redeemed / campaign.TotalVouchers * 100 : 0,
            redemptions.Count,
            vouchers.Where(v => v.ClaimedAt.HasValue).MinBy(v => v.ClaimedAt)?.ClaimedAt,
            redemptions.OrderByDescending(r => r.RedeemedAt).FirstOrDefault()?.RedeemedAt);
    }
}

/// <summary>
/// EN: Handler for GetAllVouchersQuery.
/// VI: Handler cho GetAllVouchersQuery.
/// </summary>
public class GetAllVouchersQueryHandler : IRequestHandler<GetAllVouchersQuery, PaginatedResponse<AdminVoucherListDto>>
{
    private readonly PromotionServiceContext _context;

    public GetAllVouchersQueryHandler(PromotionServiceContext context)
    {
        _context = context;
    }

    public async Task<PaginatedResponse<AdminVoucherListDto>> Handle(GetAllVouchersQuery request, CancellationToken cancellationToken)
    {
        var query = _context.Vouchers.AsQueryable();

        if (request.CampaignId.HasValue)
            query = query.Where(v => v.CampaignId == request.CampaignId.Value);

        if (request.UserId.HasValue)
            query = query.Where(v => v.OwnerId == request.UserId.Value);

        if (!string.IsNullOrEmpty(request.CodeSearch))
            query = query.Where(v => v.Code.Contains(request.CodeSearch));

        if (!string.IsNullOrEmpty(request.Status))
        {
            var statusId = request.Status.ToLower() switch
            {
                "available" => 1,
                "claimed" => 2,
                "partiallyredeemed" => 3,
                "fullyredeemed" => 4,
                "expired" => 5,
                _ => 0
            };
            if (statusId > 0)
                query = query.Where(v => v.StatusId == statusId);
        }

        var totalCount = await query.CountAsync(cancellationToken);
        var totalPages = (int)Math.Ceiling(totalCount / (double)request.PageSize);

        var items = await query
            .OrderByDescending(v => v.CreatedAt)
            .Skip((request.PageNumber - 1) * request.PageSize)
            .Take(request.PageSize)
            .Join(_context.Campaigns,
                v => v.CampaignId,
                c => c.Id,
                (v, c) => new AdminVoucherListDto(
                    v.Id,
                    v.CampaignId,
                    c.Name,
                    v.Code,
                    v.OwnerId,
                    null,
                    v.FaceValue,
                    v.RemainingValue,
                    v.StatusId == 1 ? "Available" : v.StatusId == 2 ? "Claimed" : v.StatusId == 3 ? "PartiallyRedeemed" : v.StatusId == 4 ? "FullyRedeemed" : "Expired",
                    v.ClaimedAt,
                    v.ExpiresAt,
                    v.RedeemedAt,
                    v.CreatedAt))
            .ToListAsync(cancellationToken);

        return new PaginatedResponse<AdminVoucherListDto>(items, totalCount, request.PageNumber, request.PageSize, totalPages);
    }
}

/// <summary>
/// EN: Handler for GetAllRedemptionsQuery.
/// VI: Handler cho GetAllRedemptionsQuery.
/// </summary>
public class GetAllRedemptionsQueryHandler : IRequestHandler<GetAllRedemptionsQuery, PaginatedResponse<AdminRedemptionListDto>>
{
    private readonly PromotionServiceContext _context;

    public GetAllRedemptionsQueryHandler(PromotionServiceContext context)
    {
        _context = context;
    }

    public async Task<PaginatedResponse<AdminRedemptionListDto>> Handle(GetAllRedemptionsQuery request, CancellationToken cancellationToken)
    {
        var query = _context.Redemptions.AsQueryable();

        if (request.CampaignId.HasValue)
            query = query.Where(r => r.CampaignId == request.CampaignId.Value);

        if (request.VoucherId.HasValue)
            query = query.Where(r => r.VoucherId == request.VoucherId.Value);

        if (request.UserId.HasValue)
            query = query.Where(r => r.UserId == request.UserId.Value);

        if (request.DateFrom.HasValue)
            query = query.Where(r => r.RedeemedAt >= request.DateFrom.Value);

        if (request.DateTo.HasValue)
            query = query.Where(r => r.RedeemedAt <= request.DateTo.Value);

        var totalCount = await query.CountAsync(cancellationToken);
        var totalPages = (int)Math.Ceiling(totalCount / (double)request.PageSize);

        var items = await query
            .OrderByDescending(r => r.RedeemedAt)
            .Skip((request.PageNumber - 1) * request.PageSize)
            .Take(request.PageSize)
            .Join(_context.Vouchers,
                r => r.VoucherId,
                v => v.Id,
                (r, v) => new { Redemption = r, Voucher = v })
            .Join(_context.Campaigns,
                rv => rv.Voucher.CampaignId,
                c => c.Id,
                (rv, c) => new AdminRedemptionListDto(
                    rv.Redemption.Id,
                    rv.Redemption.VoucherId,
                    rv.Voucher.Code,
                    rv.Redemption.CampaignId,
                    c.Name,
                    rv.Redemption.UserId,
                    rv.Redemption.OrderId,
                    rv.Redemption.AmountUsed,
                    rv.Redemption.AmountRefunded,
                    rv.Redemption.RedeemedAt))
            .ToListAsync(cancellationToken);

        return new PaginatedResponse<AdminRedemptionListDto>(items, totalCount, request.PageNumber, request.PageSize, totalPages);
    }
}

/// <summary>
/// EN: Handler for SearchVouchersQuery - search vouchers by code pattern.
/// VI: Handler cho SearchVouchersQuery - tìm kiếm vouchers theo mẫu mã.
/// </summary>
public class SearchVouchersQueryHandler : IRequestHandler<SearchVouchersQuery, IEnumerable<AdminVoucherListDto>>
{
    private readonly PromotionServiceContext _context;

    public SearchVouchersQueryHandler(PromotionServiceContext context)
    {
        _context = context;
    }

    public async Task<IEnumerable<AdminVoucherListDto>> Handle(SearchVouchersQuery request, CancellationToken cancellationToken)
    {
        // EN: Search vouchers by code pattern (case-insensitive)
        // VI: Tìm kiếm vouchers theo mẫu mã (không phân biệt hoa thường)
        if (string.IsNullOrWhiteSpace(request.SearchTerm))
            return Enumerable.Empty<AdminVoucherListDto>();

        var searchTerm = request.SearchTerm.Trim();

        var items = await _context.Vouchers
            .AsNoTracking()
            .Where(v => v.Code.Contains(searchTerm))
            .OrderByDescending(v => v.CreatedAt)
            .Take(50) // EN: Limit search results / VI: Giới hạn kết quả tìm kiếm
            .Join(_context.Campaigns,
                v => v.CampaignId,
                c => c.Id,
                (v, c) => new AdminVoucherListDto(
                    v.Id,
                    v.CampaignId,
                    c.Name,
                    v.Code,
                    v.OwnerId,
                    null,
                    v.FaceValue,
                    v.RemainingValue,
                    v.StatusId == 1 ? "Available" : v.StatusId == 2 ? "Claimed" : v.StatusId == 3 ? "PartiallyRedeemed" : v.StatusId == 4 ? "FullyRedeemed" : "Expired",
                    v.ClaimedAt,
                    v.ExpiresAt,
                    v.RedeemedAt,
                    v.CreatedAt))
            .ToListAsync(cancellationToken);

        return items;
    }
}

/// <summary>
/// EN: Handler for GetCampaignVouchersQuery - return vouchers for a specific campaign with pagination.
/// VI: Handler cho GetCampaignVouchersQuery - trả về vouchers của chiến dịch cụ thể với phân trang.
/// </summary>
public class GetCampaignVouchersQueryHandler : IRequestHandler<GetCampaignVouchersQuery, PaginatedResponse<AdminVoucherListDto>>
{
    private readonly PromotionServiceContext _context;

    public GetCampaignVouchersQueryHandler(PromotionServiceContext context)
    {
        _context = context;
    }

    public async Task<PaginatedResponse<AdminVoucherListDto>> Handle(GetCampaignVouchersQuery request, CancellationToken cancellationToken)
    {
        // EN: Build query for campaign vouchers / VI: Xây dựng query cho vouchers chiến dịch
        var query = _context.Vouchers
            .AsNoTracking()
            .Where(v => v.CampaignId == request.CampaignId);

        // EN: Apply status filter if provided / VI: Áp dụng filter status nếu có
        if (!string.IsNullOrEmpty(request.Status))
        {
            var statusId = request.Status.ToLower() switch
            {
                "available" => 1,
                "claimed" => 2,
                "partiallyredeemed" => 3,
                "fullyredeemed" => 4,
                "expired" => 5,
                _ => 0
            };
            if (statusId > 0)
                query = query.Where(v => v.StatusId == statusId);
        }

        var totalCount = await query.CountAsync(cancellationToken);
        var totalPages = (int)Math.Ceiling(totalCount / (double)request.PageSize);

        // EN: Get campaign name for DTO / VI: Lấy tên chiến dịch cho DTO
        var campaignName = await _context.Campaigns
            .AsNoTracking()
            .Where(c => c.Id == request.CampaignId)
            .Select(c => c.Name)
            .FirstOrDefaultAsync(cancellationToken) ?? "Unknown";

        var items = await query
            .OrderByDescending(v => v.CreatedAt)
            .Skip((request.PageNumber - 1) * request.PageSize)
            .Take(request.PageSize)
            .Select(v => new AdminVoucherListDto(
                v.Id,
                v.CampaignId,
                campaignName,
                v.Code,
                v.OwnerId,
                null,
                v.FaceValue,
                v.RemainingValue,
                v.StatusId == 1 ? "Available" : v.StatusId == 2 ? "Claimed" : v.StatusId == 3 ? "PartiallyRedeemed" : v.StatusId == 4 ? "FullyRedeemed" : "Expired",
                v.ClaimedAt,
                v.ExpiresAt,
                v.RedeemedAt,
                v.CreatedAt))
            .ToListAsync(cancellationToken);

        return new PaginatedResponse<AdminVoucherListDto>(items, totalCount, request.PageNumber, request.PageSize, totalPages);
    }
}

/// <summary>
/// EN: Handler for GetRedemptionStatisticsQuery.
/// VI: Handler cho GetRedemptionStatisticsQuery.
/// </summary>
public class GetRedemptionStatisticsQueryHandler : IRequestHandler<GetRedemptionStatisticsQuery, AdminRedemptionStatisticsDto>
{
    private readonly PromotionServiceContext _context;

    public GetRedemptionStatisticsQueryHandler(PromotionServiceContext context)
    {
        _context = context;
    }

    public async Task<AdminRedemptionStatisticsDto> Handle(GetRedemptionStatisticsQuery request, CancellationToken cancellationToken)
    {
        var query = _context.Redemptions.AsQueryable();

        if (request.CampaignId.HasValue)
            query = query.Where(r => r.CampaignId == request.CampaignId.Value);

        var today = DateTime.UtcNow.Date;
        var weekStart = today.AddDays(-(int)today.DayOfWeek);
        var monthStart = new DateTime(today.Year, today.Month, 1);

        var stats = await query
            .GroupBy(_ => 1)
            .Select(g => new
            {
                Total = g.Count(),
                TotalAmountUsed = g.Sum(r => r.AmountUsed),
                TotalAmountRefunded = g.Sum(r => r.AmountRefunded),
                AvgAmount = g.Average(r => r.AmountUsed)
            })
            .FirstOrDefaultAsync(cancellationToken);

        var todayCount = await query.CountAsync(r => r.RedeemedAt >= today, cancellationToken);
        var weekCount = await query.CountAsync(r => r.RedeemedAt >= weekStart, cancellationToken);
        var monthCount = await query.CountAsync(r => r.RedeemedAt >= monthStart, cancellationToken);

        return new AdminRedemptionStatisticsDto(
            stats?.Total ?? 0,
            stats?.TotalAmountUsed ?? 0,
            stats?.TotalAmountRefunded ?? 0,
            stats?.AvgAmount ?? 0,
            todayCount,
            weekCount,
            monthCount);
    }
}
