using System.Globalization;

namespace Meezi.API.Utils;

public static class JalaliCalendarHelper
{
    private static readonly PersianCalendar Persian = new();

    public static TimeZoneInfo IranTimeZone =>
        TimeZoneInfo.TryFindSystemTimeZoneById("Asia/Tehran", out var tz)
            ? tz
            : TimeZoneInfo.FindSystemTimeZoneById("Iran Standard Time");

    public static bool TryParseJalaliDate(string? input, out int year, out int month, out int day)
    {
        year = month = day = 0;
        if (string.IsNullOrWhiteSpace(input))
            return false;

        var normalized = input.Trim().Replace('/', '-');
        var parts = normalized.Split('-', StringSplitOptions.RemoveEmptyEntries);
        if (parts.Length != 3)
            return false;

        if (!int.TryParse(parts[0], out year) || !int.TryParse(parts[1], out month) || !int.TryParse(parts[2], out day))
            return false;

        if (year is < 1300 or > 1500 || month is < 1 or > 12 || day is < 1 or > 31)
            return false;

        try
        {
            _ = Persian.ToDateTime(year, month, day, 0, 0, 0, 0);
            return true;
        }
        catch
        {
            return false;
        }
    }

    public static bool TryParseJalaliMonth(string? input, out int year, out int month)
    {
        year = month = 0;
        if (string.IsNullOrWhiteSpace(input))
            return false;

        var normalized = input.Trim().Replace('/', '-');
        var parts = normalized.Split('-', StringSplitOptions.RemoveEmptyEntries);
        if (parts.Length != 2)
            return false;

        if (!int.TryParse(parts[0], out year) || !int.TryParse(parts[1], out month))
            return false;

        if (year is < 1300 or > 1500 || month is < 1 or > 12)
            return false;

        try
        {
            _ = Persian.GetDaysInMonth(year, month);
            return true;
        }
        catch
        {
            return false;
        }
    }

    public static string FormatJalaliDate(int year, int month, int day) =>
        $"{year}-{month:D2}-{day:D2}";

    public static string FormatJalaliMonth(int year, int month) =>
        $"{year}-{month:D2}";

    public static (int Year, int Month, int Day) TodayJalali()
    {
        var local = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, IranTimeZone);
        return (Persian.GetYear(local), Persian.GetMonth(local), Persian.GetDayOfMonth(local));
    }

    public static (DateTime UtcStart, DateTime UtcEnd) GetUtcRangeForJalaliDay(int year, int month, int day)
    {
        var localStart = Persian.ToDateTime(year, month, day, 0, 0, 0, 0);
        var localEndExclusive = localStart.AddDays(1);
        var utcStart = TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(localStart, DateTimeKind.Unspecified), IranTimeZone);
        var utcEnd = TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(localEndExclusive, DateTimeKind.Unspecified), IranTimeZone);
        return (utcStart, utcEnd);
    }

    public static (DateTime UtcStart, DateTime UtcEnd) GetUtcRangeForJalaliMonth(int year, int month)
    {
        var daysInMonth = Persian.GetDaysInMonth(year, month);
        var (utcStart, _) = GetUtcRangeForJalaliDay(year, month, 1);
        var (_, utcEnd) = GetUtcRangeForJalaliDay(year, month, daysInMonth);
        return (utcStart, utcEnd);
    }

    public static (int Year, int Month, int Day) ToJalali(DateTime utc)
    {
        var local = TimeZoneInfo.ConvertTimeFromUtc(utc, IranTimeZone);
        return (Persian.GetYear(local), Persian.GetMonth(local), Persian.GetDayOfMonth(local));
    }
}
