using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Enums;
using Microsoft.EntityFrameworkCore;

namespace CMSMicroservice.Application.CommissionCQ.Queries.GetWithdrawalReports;

/// <summary>
/// Handler برای دریافت گزارش برداشت‌ها
/// </summary>
public class GetWithdrawalReportsQueryHandler : IRequestHandler<GetWithdrawalReportsQuery, WithdrawalReportsDto>
{
    private readonly IApplicationDbContext _context;

    public GetWithdrawalReportsQueryHandler(IApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<WithdrawalReportsDto> Handle(GetWithdrawalReportsQuery request, CancellationToken cancellationToken)
    {
        // تعیین بازه زمانی پیش‌فرض (30 روز گذشته)
        var endDate = request.EndDate ?? DateTime.Now;
        var startDate = request.StartDate ?? endDate.AddDays(-30);

        // Query پایه
        var query = _context.UserCommissionPayouts
            .Where(p => p.Created >= startDate && p.Created <= endDate);

        // فیلتر بر اساس وضعیت
        if (request.Status.HasValue)
        {
            query = query.Where(p => p.Status == request.Status.Value);
        }

        // فیلتر بر اساس کاربر
        if (request.UserId.HasValue)
        {
            query = query.Where(p => p.UserId == request.UserId.Value);
        }

        var payouts = await query
            .OrderBy(p => p.Created)
            .ToListAsync(cancellationToken);

        // گروه‌بندی بر اساس نوع بازه
        var periodReports = request.PeriodType switch
        {
            ReportPeriodType.Daily => GroupByDay(payouts, startDate, endDate),
            ReportPeriodType.Weekly => GroupByWeek(payouts, startDate, endDate),
            ReportPeriodType.Monthly => GroupByMonth(payouts, startDate, endDate),
            _ => GroupByDay(payouts, startDate, endDate)
        };

        // محاسبه خلاصه کلی
        var summary = CalculateSummary(payouts);

        return new WithdrawalReportsDto
        {
            PeriodReports = periodReports,
            Summary = summary
        };
    }

    private List<PeriodReportDto> GroupByDay(List<Domain.Entities.Commission.UserCommissionPayout> payouts, DateTime startDate, DateTime endDate)
    {
        var reports = new List<PeriodReportDto>();
        var currentDate = startDate.Date;

        while (currentDate <= endDate.Date)
        {
            var dayPayouts = payouts.Where(p => p.Created.Date == currentDate).ToList();

            reports.Add(new PeriodReportDto
            {
                PeriodLabel = currentDate.ToString("yyyy-MM-dd"),
                StartDate = currentDate,
                EndDate = currentDate.AddDays(1).AddSeconds(-1),
                TotalRequests = dayPayouts.Count,
                PendingCount = dayPayouts.Count(p => p.Status == CommissionPayoutStatus.Pending || 
                                                       p.Status == CommissionPayoutStatus.WithdrawRequested),
                ApprovedCount = 0, // تایید جداگانه نداریم
                RejectedCount = dayPayouts.Count(p => p.Status == CommissionPayoutStatus.Cancelled),
                CompletedCount = dayPayouts.Count(p => p.Status == CommissionPayoutStatus.Withdrawn),
                FailedCount = dayPayouts.Count(p => p.Status == CommissionPayoutStatus.PaymentFailed),
                TotalAmount = dayPayouts.Sum(p => p.TotalAmount),
                PaidAmount = dayPayouts.Where(p => p.Status == CommissionPayoutStatus.Withdrawn).Sum(p => p.TotalAmount),
                PendingAmount = dayPayouts.Where(p => p.Status == CommissionPayoutStatus.Pending || 
                                                       p.Status == CommissionPayoutStatus.WithdrawRequested).Sum(p => p.TotalAmount)
            });

            currentDate = currentDate.AddDays(1);
        }

        return reports;
    }

    private List<PeriodReportDto> GroupByWeek(List<Domain.Entities.Commission.UserCommissionPayout> payouts, DateTime startDate, DateTime endDate)
    {
        var reports = new List<PeriodReportDto>();
        var currentWeekStart = startDate.Date;

        int weekNumber = 1;
        while (currentWeekStart <= endDate)
        {
            var weekEnd = currentWeekStart.AddDays(7).AddSeconds(-1);
            if (weekEnd > endDate)
                weekEnd = endDate;

            var weekPayouts = payouts.Where(p => p.Created >= currentWeekStart && p.Created <= weekEnd).ToList();

            reports.Add(new PeriodReportDto
            {
                PeriodLabel = $"هفته {weekNumber}",
                StartDate = currentWeekStart,
                EndDate = weekEnd,
                TotalRequests = weekPayouts.Count,
                PendingCount = weekPayouts.Count(p => p.Status == CommissionPayoutStatus.Pending || 
                                                       p.Status == CommissionPayoutStatus.WithdrawRequested),
                ApprovedCount = 0, // تایید جداگانه نداریم
                RejectedCount = weekPayouts.Count(p => p.Status == CommissionPayoutStatus.Cancelled),
                CompletedCount = weekPayouts.Count(p => p.Status == CommissionPayoutStatus.Withdrawn),
                FailedCount = weekPayouts.Count(p => p.Status == CommissionPayoutStatus.PaymentFailed),
                TotalAmount = weekPayouts.Sum(p => p.TotalAmount),
                PaidAmount = weekPayouts.Where(p => p.Status == CommissionPayoutStatus.Withdrawn).Sum(p => p.TotalAmount),
                PendingAmount = weekPayouts.Where(p => p.Status == CommissionPayoutStatus.Pending || 
                                                        p.Status == CommissionPayoutStatus.WithdrawRequested).Sum(p => p.TotalAmount)
            });

            currentWeekStart = currentWeekStart.AddDays(7);
            weekNumber++;
        }

        return reports;
    }

    private List<PeriodReportDto> GroupByMonth(List<Domain.Entities.Commission.UserCommissionPayout> payouts, DateTime startDate, DateTime endDate)
    {
        var reports = new List<PeriodReportDto>();
        var currentMonthStart = new DateTime(startDate.Year, startDate.Month, 1);

        while (currentMonthStart <= endDate)
        {
            var monthEnd = currentMonthStart.AddMonths(1).AddSeconds(-1);
            if (monthEnd > endDate)
                monthEnd = endDate;

            var monthPayouts = payouts.Where(p => p.Created >= currentMonthStart && p.Created <= monthEnd).ToList();

            var persianMonthName = GetPersianMonthName(currentMonthStart.Month);

            reports.Add(new PeriodReportDto
            {
                PeriodLabel = $"{persianMonthName} {currentMonthStart.Year}",
                StartDate = currentMonthStart,
                EndDate = monthEnd,
                TotalRequests = monthPayouts.Count,
                PendingCount = monthPayouts.Count(p => p.Status == CommissionPayoutStatus.Pending || 
                                                       p.Status == CommissionPayoutStatus.WithdrawRequested),
                ApprovedCount = 0, // تایید جداگانه نداریم
                RejectedCount = monthPayouts.Count(p => p.Status == CommissionPayoutStatus.Cancelled),
                CompletedCount = monthPayouts.Count(p => p.Status == CommissionPayoutStatus.Withdrawn),
                FailedCount = monthPayouts.Count(p => p.Status == CommissionPayoutStatus.PaymentFailed),
                TotalAmount = monthPayouts.Sum(p => p.TotalAmount),
                PaidAmount = monthPayouts.Where(p => p.Status == CommissionPayoutStatus.Withdrawn).Sum(p => p.TotalAmount),
                PendingAmount = monthPayouts.Where(p => p.Status == CommissionPayoutStatus.Pending || 
                                                         p.Status == CommissionPayoutStatus.WithdrawRequested).Sum(p => p.TotalAmount)
            });

            currentMonthStart = currentMonthStart.AddMonths(1);
        }

        return reports;
    }

    private WithdrawalSummaryDto CalculateSummary(List<Domain.Entities.Commission.UserCommissionPayout> payouts)
    {
        var totalRequests = payouts.Count;
        var completedCount = payouts.Count(p => p.Status == CommissionPayoutStatus.Withdrawn);

        return new WithdrawalSummaryDto
        {
            TotalRequests = totalRequests,
            TotalAmount = payouts.Sum(p => p.TotalAmount),
            TotalPaid = payouts.Where(p => p.Status == CommissionPayoutStatus.Withdrawn).Sum(p => p.TotalAmount),
            TotalPending = payouts.Where(p => p.Status == CommissionPayoutStatus.Pending || 
                                              p.Status == CommissionPayoutStatus.WithdrawRequested).Sum(p => p.TotalAmount),
            TotalRejected = payouts.Where(p => p.Status == CommissionPayoutStatus.Cancelled).Sum(p => p.TotalAmount),
            AverageAmount = totalRequests > 0 ? payouts.Sum(p => p.TotalAmount) / totalRequests : 0,
            UniqueUsers = payouts.Select(p => p.UserId).Distinct().Count(),
            SuccessRate = totalRequests > 0 ? (decimal)completedCount / totalRequests * 100 : 0
        };
    }

    private string GetPersianMonthName(int month)
    {
        return month switch
        {
            1 => "فروردین",
            2 => "اردیبهشت",
            3 => "خرداد",
            4 => "تیر",
            5 => "مرداد",
            6 => "شهریور",
            7 => "مهر",
            8 => "آبان",
            9 => "آذر",
            10 => "دی",
            11 => "بهمن",
            12 => "اسفند",
            _ => month.ToString()
        };
    }
}
