using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using CMSMicroservice.Domain.Entities;
using CMSMicroservice.Infrastructure.Persistence;
using System.Globalization;
using System.Collections.Generic;

namespace CMSMicroservice.Infrastructure.Data.Seeding;

/// <summary>
/// Seeder برای پر کردن جدول WeekDefinitions
/// هفته اول سیستم: 2025-W46 میلادی = 1404-W35 شمسی
/// شروع هفته شمسی: شنبه | شروع هفته میلادی ISO: دوشنبه
/// </summary>
public class WeekDefinitionSeeder
{
    private readonly ApplicationDbContext _context;
    private readonly ILogger<WeekDefinitionSeeder> _logger;
    private readonly PersianCalendar _persianCalendar;
    
    /// <summary>
    /// تاریخ شروع هفته اول سیستم (شنبه 17 آبان 1404 = 8 نوامبر 2025)
    /// این شنبه در هفته 46 میلادی (ISO) و هفته 35 شمسی قرار دارد
    /// </summary>
    private static readonly DateTime BaseWeekStartDate = new DateTime(2025, 11, 8); // شنبه 8 نوامبر 2025

    public WeekDefinitionSeeder(
        ApplicationDbContext context,
        ILogger<WeekDefinitionSeeder> logger)
    {
        _context = context;
        _logger = logger;
        _persianCalendar = new PersianCalendar();
    }

    /// <summary>
    /// Seed هفته‌ها از هفته مبدأ تا یک سال بعد
    /// </summary>
    public async Task SeedAsync(CancellationToken cancellationToken = default)
    {
        _logger.LogInformation("=== WeekDefinitionSeeder: Starting ===");
        _logger.LogInformation("Base week starts at: {BaseDate:yyyy-MM-dd} (Saturday)", BaseWeekStartDate);

        try
        {
            var existingCount = await _context.WeekDefinitions.CountAsync(cancellationToken);
            
            if (existingCount > 0)
            {
                _logger.LogInformation("WeekDefinitions already has {Count} records. Checking for missing weeks...", existingCount);
            }

            // شروع از هفته مبدأ
            var startDate = BaseWeekStartDate;
            // پایان تا یک سال بعد از الان
            var endDate = GetEndOfWeek(DateTime.Today.AddYears(1));

            var currentDate = startDate;
            var addedCount = 0;

            while (currentDate <= endDate)
            {
                var weekStart = currentDate; // شنبه
                var weekEnd = currentDate.AddDays(6).Date.AddHours(23).AddMinutes(59).AddSeconds(59); // جمعه 23:59:59

                // محاسبه شماره ترتیبی هفته (از 1 شروع می‌شود)
                var weekOrder = CalculateWeekOrder(weekStart);
                
                // نام نمایشی: هفته یکم، هفته دوم، ...
                var displayName = GetPersianOrdinalWeekName(weekOrder);
                
                // شماره هفته میلادی ISO (بر اساس دوشنبه)
                var gregorianWeekNumber = GetISOWeekNumber(weekStart);
                
                // شماره هفته شمسی (بر اساس شنبه)
                var persianWeekNumber = GetPersianWeekNumber(weekStart);

                // چک کنیم که این هفته قبلاً ثبت نشده باشه
                var exists = await _context.WeekDefinitions
                    .AnyAsync(w => w.StartDate == weekStart, cancellationToken);

                if (!exists)
                {
                    var weekDefinition = new WeekDefinition
                    {
                        WeekOrder = weekOrder,
                        DisplayName = displayName,
                        GregorianWeekNumber = gregorianWeekNumber,
                        PersianWeekNumber = persianWeekNumber,
                        StartDate = weekStart,
                        EndDate = weekEnd,
                        GregorianYear = weekStart.Year,
                        PersianYear = _persianCalendar.GetYear(weekStart),
                        IsActive = true
                    };

                    _context.WeekDefinitions.Add(weekDefinition);
                    addedCount++;

                    _logger.LogDebug("Added: {DisplayName} | {GregorianWeek} | {PersianWeek} | {Start:yyyy-MM-dd} - {End:yyyy-MM-dd}",
                        displayName, gregorianWeekNumber, persianWeekNumber, weekStart, weekEnd);
                }

                // رفتن به هفته بعد (7 روز)
                currentDate = currentDate.AddDays(7);
            }

            if (addedCount > 0)
            {
                await _context.SaveChangesAsync(cancellationToken);
                _logger.LogInformation("Successfully added {Count} week definitions", addedCount);
            }
            else
            {
                _logger.LogInformation("No new week definitions needed");
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error seeding WeekDefinitions");
            throw;
        }

        _logger.LogInformation("=== WeekDefinitionSeeder: Completed ===");
    }
    
    /// <summary>
    /// محاسبه شماره ترتیبی هفته بر اساس فاصله از هفته مبدأ
    /// هفته مبدأ = 1، هفته بعد = 2، ...
    /// </summary>
    private static int CalculateWeekOrder(DateTime weekStartDate)
    {
        var daysDiff = (weekStartDate - BaseWeekStartDate).Days;
        var weeksDiff = daysDiff / 7;
        return weeksDiff + 1;
    }
    
    /// <summary>
    /// تبدیل عدد به نام ترتیبی فارسی
    /// 1 -> هفته یکم، 2 -> هفته دوم، ...
    /// </summary>
    private static string GetPersianOrdinalWeekName(int weekOrder)
    {
        var ordinals = new Dictionary<int, string>
        {
            { 1, "یکم" }, { 2, "دوم" }, { 3, "سوم" }, { 4, "چهارم" }, { 5, "پنجم" },
            { 6, "ششم" }, { 7, "هفتم" }, { 8, "هشتم" }, { 9, "نهم" }, { 10, "دهم" },
            { 11, "یازدهم" }, { 12, "دوازدهم" }, { 13, "سیزدهم" }, { 14, "چهاردهم" }, { 15, "پانزدهم" },
            { 16, "شانزدهم" }, { 17, "هفدهم" }, { 18, "هجدهم" }, { 19, "نوزدهم" }, { 20, "بیستم" },
            { 21, "بیست و یکم" }, { 22, "بیست و دوم" }, { 23, "بیست و سوم" }, { 24, "بیست و چهارم" }, { 25, "بیست و پنجم" },
            { 26, "بیست و ششم" }, { 27, "بیست و هفتم" }, { 28, "بیست و هشتم" }, { 29, "بیست و نهم" }, { 30, "سی‌ام" },
            { 31, "سی و یکم" }, { 32, "سی و دوم" }, { 33, "سی و سوم" }, { 34, "سی و چهارم" }, { 35, "سی و پنجم" },
            { 36, "سی و ششم" }, { 37, "سی و هفتم" }, { 38, "سی و هشتم" }, { 39, "سی و نهم" }, { 40, "چهلم" },
            { 41, "چهل و یکم" }, { 42, "چهل و دوم" }, { 43, "چهل و سوم" }, { 44, "چهل و چهارم" }, { 45, "چهل و پنجم" },
            { 46, "چهل و ششم" }, { 47, "چهل و هفتم" }, { 48, "چهل و هشتم" }, { 49, "چهل و نهم" }, { 50, "پنجاهم" },
            { 51, "پنجاه و یکم" }, { 52, "پنجاه و دوم" }, { 53, "پنجاه و سوم" }, { 54, "پنجاه و چهارم" }, { 55, "پنجاه و پنجم" },
            { 56, "پنجاه و ششم" }, { 57, "پنجاه و هفتم" }, { 58, "پنجاه و هشتم" }, { 59, "پنجاه و نهم" }, { 60, "شصتم" }
        };

        if (ordinals.TryGetValue(weekOrder, out var ordinal))
        {
            return $"هفته {ordinal}";
        }
        
        // برای اعداد بزرگتر از 60
        return $"هفته {weekOrder}";
    }

    /// <summary>
    /// پیدا کردن شنبه این هفته (شروع هفته شمسی)
    /// </summary>
    private static DateTime GetStartOfWeek(DateTime date)
    {
        var diff = (7 + (date.DayOfWeek - DayOfWeek.Saturday)) % 7;
        return date.AddDays(-diff).Date;
    }

    /// <summary>
    /// پیدا کردن جمعه این هفته (پایان هفته شمسی)
    /// </summary>
    private static DateTime GetEndOfWeek(DateTime date)
    {
        return GetStartOfWeek(date).AddDays(6);
    }

    /// <summary>
    /// شماره هفته میلادی ISO 8601
    /// هفته ISO از دوشنبه شروع می‌شود
    /// برای تاریخ شنبه، باید دوشنبه همان هفته را پیدا کنیم (2 روز بعد)
    /// </summary>
    private static string GetISOWeekNumber(DateTime saturdayDate)
    {
        // شنبه + 2 روز = دوشنبه همان هفته ISO
        var mondayOfWeek = saturdayDate.AddDays(2);
        
        // محاسبه شماره هفته ISO
        var cal = CultureInfo.InvariantCulture.Calendar;
        var weekNumber = cal.GetWeekOfYear(mondayOfWeek, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
        var year = mondayOfWeek.Year;
        
        // تصحیح سال برای هفته‌های لبه‌ای
        if (weekNumber == 1 && mondayOfWeek.Month == 12)
        {
            year++;
        }
        else if (weekNumber >= 52 && mondayOfWeek.Month == 1)
        {
            year--;
        }
        
        return $"{year}-W{weekNumber:D2}";
    }

    /// <summary>
    /// شماره هفته شمسی
    /// هفته شمسی از شنبه شروع می‌شود
    /// </summary>
    private string GetPersianWeekNumber(DateTime saturdayDate)
    {
        var persianYear = _persianCalendar.GetYear(saturdayDate);
        var dayOfYear = _persianCalendar.GetDayOfYear(saturdayDate);
        
        // اول فروردین این سال
        var firstDayOfYear = _persianCalendar.ToDateTime(persianYear, 1, 1, 0, 0, 0, 0);
        
        // پیدا کردن اولین شنبه سال
        var daysUntilFirstSaturday = ((int)DayOfWeek.Saturday - (int)firstDayOfYear.DayOfWeek + 7) % 7;
        
        // اگر اول فروردین شنبه باشه، daysUntilFirstSaturday = 0
        // در غیر این صورت روزهای قبل از اولین شنبه، هفته 0 یا هفته آخر سال قبل هستند
        
        int weekNumber;
        if (daysUntilFirstSaturday == 0)
        {
            // اول فروردین شنبه است
            weekNumber = ((dayOfYear - 1) / 7) + 1;
        }
        else
        {
            // روزهای قبل از اولین شنبه
            if (dayOfYear <= daysUntilFirstSaturday)
            {
                // این روزها متعلق به آخرین هفته سال قبل هستند
                // اما برای سادگی، هفته 1 در نظر می‌گیریم
                weekNumber = 1;
            }
            else
            {
                weekNumber = ((dayOfYear - daysUntilFirstSaturday - 1) / 7) + 1;
            }
        }
        
        return $"{persianYear}-W{weekNumber:D2}";
    }
}
