using System.Globalization;

namespace BackOffice.Services;

/// <summary>
/// سرویس تبدیل و نمایش تاریخ شمسی
/// </summary>
public interface IPersianDateTimeService
{
    /// <summary>
    /// دریافت شماره هفته فعلی به فرمت شمسی
    /// فرمت: "1404-W23"
    /// </summary>
    string GetCurrentWeekNumber();

    /// <summary>
    /// تبدیل شماره هفته میلادی به شمسی
    /// ورودی: "2025-W48" (میلادی)
    /// خروجی: "1404-W23" (شمسی)
    /// </summary>
    string ConvertWeekNumberToPersian(string gregorianWeekNumber);

    /// <summary>
    /// تبدیل تاریخ میلادی به شمسی با فرمت کامل
    /// خروجی: "1404/09/21"
    /// </summary>
    string ConvertToPersianDate(DateTime dateTime);

    /// <summary>
    /// تبدیل تاریخ میلادی به شمسی با فرمت کامل + ساعت
    /// خروجی: "1404/09/21 - 14:30"
    /// </summary>
    string ConvertToPersianDateTime(DateTime dateTime);

    /// <summary>
    /// نمایش بازه هفته به شمسی
    /// خروجی: "شنبه 1404/09/15 تا جمعه 1404/09/21"
    /// </summary>
    string GetWeekRangeDisplay(string gregorianWeekNumber);
}

public class PersianDateTimeService : IPersianDateTimeService
{
    private readonly PersianCalendar _persianCalendar = new();
    private static readonly string[] PersianDayNames = 
        { "یکشنبه", "دوشنبه", "سه‌شنبه", "چهارشنبه", "پنج‌شنبه", "جمعه", "شنبه" };

    public string GetCurrentWeekNumber()
    {
        var now = DateTime.Now;
        return GetPersianWeekNumber(now);
    }

    public string ConvertWeekNumberToPersian(string gregorianWeekNumber)
    {
        try
        {
            // Parse: "2025-W48"
            var parts = gregorianWeekNumber.Split('-');
            if (parts.Length != 2 || !parts[1].StartsWith("W"))
                return gregorianWeekNumber;

            var year = int.Parse(parts[0]);
            var week = int.Parse(parts[1].Replace("W", ""));

            // محاسبه تاریخ میلادی این هفته
            var jan1 = new DateTime(year, 1, 1);
            var jan1DayOfWeek = (int)jan1.DayOfWeek;
            var daysToFirstSaturday = jan1DayOfWeek == 6 ? 0 : (6 - jan1DayOfWeek + 7) % 7;
            var firstSaturday = jan1.AddDays(daysToFirstSaturday);
            var weekStart = firstSaturday.AddDays((week - 1) * 7);

            // تبدیل به شمسی
            return GetPersianWeekNumber(weekStart);
        }
        catch
        {
            return gregorianWeekNumber;
        }
    }

    public string ConvertToPersianDate(DateTime dateTime)
    {
        var year = _persianCalendar.GetYear(dateTime);
        var month = _persianCalendar.GetMonth(dateTime);
        var day = _persianCalendar.GetDayOfMonth(dateTime);
        
        return $"{year:0000}/{month:00}/{day:00}";
    }

    public string ConvertToPersianDateTime(DateTime dateTime)
    {
        var persianDate = ConvertToPersianDate(dateTime);
        var time = dateTime.ToString("HH:mm");
        
        return $"{persianDate} - {time}";
    }

    public string GetWeekRangeDisplay(string gregorianWeekNumber)
    {
        try
        {
            var (startDate, endDate) = ParseGregorianWeekNumber(gregorianWeekNumber);
            
            var startPersian = ConvertToPersianDate(startDate);
            var endPersian = ConvertToPersianDate(endDate);
            
            var startDay = PersianDayNames[(int)startDate.DayOfWeek];
            var endDay = PersianDayNames[(int)endDate.DayOfWeek];
            
            return $"{startDay} {startPersian} تا {endDay} {endPersian}";
        }
        catch
        {
            return gregorianWeekNumber;
        }
    }

    /// <summary>
    /// محاسبه شماره هفته شمسی (شنبه محور)
    /// </summary>
    private string GetPersianWeekNumber(DateTime dateTime)
    {
        var persianYear = _persianCalendar.GetYear(dateTime);
        var dayOfYear = _persianCalendar.GetDayOfYear(dateTime);
        
        // محاسبه اولین روز سال شمسی
        var firstDayOfYear = _persianCalendar.ToDateTime(persianYear, 1, 1, 0, 0, 0, 0);
        var firstDayOfWeek = (int)firstDayOfYear.DayOfWeek;
        
        // محاسبه تعداد روزها تا اولین شنبه
        // شنبه = 6, یکشنبه = 0, دوشنبه = 1, ...
        var daysToFirstSaturday = firstDayOfWeek == 6 ? 0 : (6 - firstDayOfWeek + 7) % 7;
        
        // محاسبه شماره هفته
        var adjustedDayOfYear = dayOfYear + daysToFirstSaturday - 1;
        var weekNumber = (adjustedDayOfYear / 7) + 1;
        
        return $"{persianYear}-W{weekNumber:D2}";
    }

    /// <summary>
    /// تبدیل شماره هفته میلادی به بازه تاریخی
    /// </summary>
    private (DateTime startDate, DateTime endDate) ParseGregorianWeekNumber(string weekNumber)
    {
        var parts = weekNumber.Split('-');
        var year = int.Parse(parts[0]);
        var week = int.Parse(parts[1].Replace("W", ""));

        var jan1 = new DateTime(year, 1, 1);
        var jan1DayOfWeek = (int)jan1.DayOfWeek;
        var daysToFirstSaturday = jan1DayOfWeek == 6 ? 0 : (6 - jan1DayOfWeek + 7) % 7;
        var firstSaturday = jan1.AddDays(daysToFirstSaturday);
        
        var weekStart = firstSaturday.AddDays((week - 1) * 7);
        var weekEnd = weekStart.AddDays(6);
        
        return (weekStart, weekEnd);
    }
}
