using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Enums;
using Microsoft.EntityFrameworkCore;
using System.Globalization;
using DateTimeConverterCL;

namespace CMSMicroservice.Application.CommissionCQ.Queries.GetAvailableWeeks;

public class GetAvailableWeeksQueryHandler : IRequestHandler<GetAvailableWeeksQuery, GetAvailableWeeksResponseDto>
{
    private readonly IApplicationDbContext _context;
    private readonly IWeekDefinitionRepository _weekDefinitionRepository;

    public GetAvailableWeeksQueryHandler(
        IApplicationDbContext context,
        IWeekDefinitionRepository weekDefinitionRepository)
    {
        _context = context;
        _weekDefinitionRepository = weekDefinitionRepository;
    }

    public async Task<GetAvailableWeeksResponseDto> Handle(
        GetAvailableWeeksQuery request, 
        CancellationToken cancellationToken)
    {
        var currentWeekDef = _weekDefinitionRepository.GetCurrentWeek();
        // var currentWeekNumber = currentWeekDef != null 
        //     ? $"{currentWeekDef.GregorianYear}-W{currentWeekDef.GregorianWeekNumber:D2}" 
        //     : _weekDefinitionRepository.CalculateGregorianWeekNumber(DateTime.Now);
        
        // دریافت هفته‌های محاسبه شده از دیتابیس
        var calculatedPools = await _context.WeeklyCommissionPools
            .Where(p => p.IsCalculated)
            .OrderByDescending(p => p.WeekDefinitionId)
            .Take(request.PastWeeksCount)
            .ToListAsync(cancellationToken);

        // دریافت لاگ‌های اجرا
        var executionLogs = await _context.WorkerExecutionLogs
            .Where(log => log.Status == WorkerExecutionStatus.Success || 
                          log.Status == WorkerExecutionStatus.Failed)
            .GroupBy(log => log.WeekDefinitionId)
            .Select(g => new 
            { 
                WeekNumber = g.Key, 
                LastLog = g.OrderByDescending(l => l.StartedAt).First()
            })
            .ToDictionaryAsync(x => x.WeekNumber, x => x.LastLog, cancellationToken);

        var allWeeks = new List<WeekInfoDto>();
        
        // هفته جاری
        var currentDate = DateTime.Now;
        var currentWeekInfo = CreateWeekInfo(currentDate, currentWeekDef.Id, calculatedPools, executionLogs);
        
        // هفته‌های گذشته (12 هفته)
        var pastWeeks = new List<WeekInfoDto>();
        for (int i = 1; i <= request.PastWeeksCount; i++)
        {
            var pastDate = currentDate.AddDays(-7 * i);
            var weekNumber = GetWeekNumberUsingRepository(pastDate);
            pastWeeks.Add(CreateWeekInfo(pastDate, currentWeekDef.Id, calculatedPools, executionLogs));
        }
        
        // هفته‌های آینده (4 هفته)
        var futureWeeks = new List<WeekInfoDto>();
        for (int i = 1; i <= request.FutureWeeksCount; i++)
        {
            var futureDate = currentDate.AddDays(7 * i);
            var weekNumber = GetWeekNumberUsingRepository(futureDate);
            futureWeeks.Add(CreateWeekInfo(futureDate, currentWeekDef.Id, calculatedPools, executionLogs));
        }
        
        // تفکیک به calculated و pending
        var calculatedWeeks = pastWeeks.Where(w => w.IsCalculated).ToList();
        var pendingWeeks = pastWeeks.Where(w => !w.IsCalculated).ToList();
        
        return new GetAvailableWeeksResponseDto
        {
            CurrentWeek = currentWeekInfo,
            CalculatedWeeks = calculatedWeeks,
            PendingWeeks = pendingWeeks,
            FutureWeeks = futureWeeks
        };
    }

    private string GetWeekNumberUsingRepository(DateTime date)
    {
        var weekDef = _weekDefinitionRepository.GetWeekByDate(date);
        if (weekDef != null)
        {
            return $"{weekDef.GregorianYear}-W{weekDef.GregorianWeekNumber:D2}";
        }
        
        // Fallback to manual calculation if not in cache
        return _weekDefinitionRepository.CalculateGregorianWeekNumber(date);
    }

    private WeekInfoDto CreateWeekInfo(
        DateTime date, 
        long weekDefinitionId,
        List<Domain.Entities.Commission.WeeklyCommissionPool> calculatedPools,
        Dictionary<long, Domain.Entities.Commission.WorkerExecutionLog> executionLogs)
    {
        // Get the actual WeekDefinition for this date
        var weekDef = _weekDefinitionRepository.GetWeekByDate(date);
        var actualWeekDefId = weekDef?.Id ?? weekDefinitionId;
        
        var (startDate, endDate) = GetWeekRange(date);
        var pool = calculatedPools.FirstOrDefault(p => p.WeekDefinitionId == actualWeekDefId);
        var log = executionLogs.GetValueOrDefault(actualWeekDefId);
        
        var isCalculated = pool != null && pool.IsCalculated;
        //
        // // تبدیل تاریخ به شمسی برای نمایش
        // var persianCalendar = new PersianCalendar();
        // var startDatePersian = $"{persianCalendar.GetYear(startDate):D4}/{persianCalendar.GetMonth(startDate):D2}/{persianCalendar.GetDayOfMonth(startDate):D2}";
        // var endDatePersian = $"{persianCalendar.GetYear(endDate):D4}/{persianCalendar.GetMonth(endDate):D2}/{persianCalendar.GetDayOfMonth(endDate):D2}";
        
        // استفاده از DisplayName از WeekDefinition
        var displayName = weekDef?.DisplayName ?? GetDisplayName(startDate);
        string displayText;
        
        if (!string.IsNullOrEmpty(displayName))
        {
            // اگر DisplayName وجود داشت، از آن استفاده کن
            displayText = $"{displayName} ({startDate.MiladiToJalali()} - {endDate.MiladiToJalali()})";
        }
        else
        {
            // تبدیل weekNumber به شمسی فقط برای نمایش (fallback)
            var persianWeekNumber = _weekDefinitionRepository.GetWeekById(actualWeekDefId);
            displayText = $"{persianWeekNumber?.PersianWeekNumber} ({startDate.MiladiToJalali()} - {endDate.MiladiToJalali()})";
        }
        
        if (isCalculated)
        {
            displayText += " ✅ محاسبه شده";
        }
        
        return new WeekInfoDto
        {
            WeekDefinitionId = actualWeekDefId,
            DisplayName = weekDef?.DisplayName ?? _weekDefinitionRepository.GetWeekById(actualWeekDefId)?.DisplayName ?? "", // میلادی (برای API)
            StartDate = startDate,
            EndDate = endDate,
            IsCalculated = isCalculated,
            CalculatedAt = pool?.CalculatedAt,
            LastExecutionStatus = log?.Status.ToString(),
            TotalPoolAmount = pool?.TotalPoolAmount,
            EligibleUsersCount = pool?.UserCommissionPayouts?.Count ?? 0,
            DisplayText = displayText // شمسی (برای نمایش)
        };
    }

    private (DateTime startDate, DateTime endDate) GetWeekRange(DateTime date)
    {
        // First try to get from repository
        var weekDef = _weekDefinitionRepository.GetWeekByDate(date);
        if (weekDef != null)
        {
            return (weekDef.StartDate, weekDef.EndDate);
        }
        
        // Fallback: manual calculation
        var dayOfWeek = (int)date.DayOfWeek;
        // محاسبه تعداد روزهایی که باید عقب برویم تا به شنبه برسیم
        // شنبه = 6, یکشنبه = 0, دوشنبه = 1, ..., جمعه = 5
        var daysToSaturday = dayOfWeek == 6 ? 0 : (dayOfWeek + 1) % 7;
        
        var startDate = date.Date.AddDays(-daysToSaturday);
        var endDate = startDate.AddDays(6).AddHours(23).AddMinutes(59).AddSeconds(59);
        
        return (startDate, endDate);
    }

    private string GetDisplayName(DateTime date)
    {
        var weekDef = _weekDefinitionRepository.GetWeekByDate(date);
        return weekDef?.DisplayName ?? "";
    }

    // private static string ConvertWeekNumberToPersian(string gregorianWeekNumber, DateTime weekStartDate)
    // {
    //     // ورودی: "2025-W48"
    //     var persianCalendar = new PersianCalendar();
    //     var persianYear = persianCalendar.GetYear(weekStartDate);
    //     
    //     // محاسبه شماره هفته شمسی
    //     var jan1Persian = persianCalendar.ToDateTime(persianYear, 1, 1, 0, 0, 0, 0);
    //     
    //     // پیدا کردن اولین شنبه سال شمسی
    //     var jan1DayOfWeek = (int)jan1Persian.DayOfWeek;
    //     var daysToFirstSaturday = jan1DayOfWeek == 6 ? 0 : (6 - jan1DayOfWeek + 7) % 7;
    //     var firstSaturday = jan1Persian.AddDays(daysToFirstSaturday);
    //     
    //     // محاسبه شماره هفته
    //     int weekNum;
    //     if (weekStartDate < firstSaturday)
    //     {
    //         weekNum = 1;
    //     }
    //     else
    //     {
    //         var daysSinceFirstSaturday = (weekStartDate - firstSaturday).Days;
    //         weekNum = (daysSinceFirstSaturday / 7) + 1;
    //     }
    //     
    //     return $"{persianYear}-W{weekNum:D2}";
    // }
}
