using System.Globalization;
using JobsMedical.Web.Models;

namespace JobsMedical.Web.Services;

/// <summary>
/// Jalali (Shamsi) date helpers built on .NET's <see cref="PersianCalendar"/>.
/// Rule of thumb in this app: store Gregorian, display Jalali.
/// </summary>
public static class JalaliDate
{
    private static readonly PersianCalendar Pc = new();

    private static readonly string[] MonthNames =
    {
        "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور",
        "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند"
    };

    // PersianCalendar: 0=Saturday .. 6=Friday is NOT how DayOfWeek maps; map explicitly.
    private static readonly string[] WeekDayNames =
    {
        "شنبه", "یکشنبه", "دوشنبه", "سه‌شنبه", "چهارشنبه", "پنجشنبه", "جمعه"
    };

    private static readonly char[] PersianDigits = { '۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹' };

    /// <summary>Convert Latin digits in a string to Persian digits.</summary>
    public static string ToPersianDigits(string input)
    {
        var chars = input.ToCharArray();
        for (var i = 0; i < chars.Length; i++)
            if (chars[i] is >= '0' and <= '9')
                chars[i] = PersianDigits[chars[i] - '0'];
        return new string(chars);
    }

    /// <summary>e.g. "۱۵ خرداد ۱۴۰۵".</summary>
    public static string ToLongDate(DateOnly date)
    {
        var dt = date.ToDateTime(TimeOnly.MinValue);
        var y = Pc.GetYear(dt);
        var m = Pc.GetMonth(dt);
        var d = Pc.GetDayOfMonth(dt);
        return ToPersianDigits($"{d} {MonthNames[m - 1]} {y}");
    }

    /// <summary>e.g. "۱۴۰۵/۰۳/۱۵".</summary>
    public static string ToShortDate(DateOnly date)
    {
        var dt = date.ToDateTime(TimeOnly.MinValue);
        return ToPersianDigits($"{Pc.GetYear(dt):0000}/{Pc.GetMonth(dt):00}/{Pc.GetDayOfMonth(dt):00}");
    }

    /// <summary>Persian weekday name, e.g. "سه‌شنبه".</summary>
    public static string WeekDayName(DateOnly date)
    {
        var dt = date.ToDateTime(TimeOnly.MinValue);
        return WeekDayNames[WeekIndex(dt)];
    }

    /// <summary>0 = Saturday (شنبه, start of Persian week) .. 6 = Friday (جمعه).</summary>
    public static int WeekIndex(DateTime dt) => ((int)dt.DayOfWeek + 1) % 7;

    /// <summary>The Saturday that starts the Persian week containing <paramref name="date"/>.</summary>
    public static DateOnly StartOfPersianWeek(DateOnly date)
    {
        var idx = WeekIndex(date.ToDateTime(TimeOnly.MinValue));
        return date.AddDays(-idx);
    }

    /// <summary>Persian month name for a given date.</summary>
    public static string MonthName(DateOnly date)
    {
        var dt = date.ToDateTime(TimeOnly.MinValue);
        return MonthNames[Pc.GetMonth(dt) - 1];
    }

    /// <summary>Just the day-of-month in Persian digits.</summary>
    public static string DayOfMonth(DateOnly date)
    {
        var dt = date.ToDateTime(TimeOnly.MinValue);
        return ToPersianDigits(Pc.GetDayOfMonth(dt).ToString());
    }

    /// <summary>Format a time as "۰۸:۰۰".</summary>
    public static string Time(TimeOnly t) => ToPersianDigits(t.ToString("HH\\:mm"));

    /// <summary>Format a Toman amount, e.g. "۱٬۵۰۰٬۰۰۰ تومان" or "توافقی" if null.</summary>
    public static string Toman(long? amount)
        => amount is null ? "توافقی" : ToPersianDigits(amount.Value.ToString("#,0")) + " تومان";

    /// <summary>
    /// Human compensation label covering all models: fixed/hourly amount, profit-share %, or
    /// BOTH (shown as "… یا … (به انتخاب شما)"), falling back to "توافقی". This is how Iranian
    /// shifts are actually advertised — a fixed كارانه, a درصد سهم درآمد, or a choice between them.
    /// </summary>
    public static string PayLabel(PayType payType, long? amount, int? sharePercent)
    {
        var parts = new List<string>();
        if (amount is not null)
            parts.Add(ToPersianDigits(amount.Value.ToString("#,0")) + " تومان" + (payType == PayType.PerHour ? " (ساعتی)" : ""));
        if (sharePercent is not null)
            parts.Add(ToPersianDigits(sharePercent.Value.ToString()) + "٪ سهم درآمد");

        if (parts.Count == 0) return "توافقی";
        if (parts.Count > 1) return string.Join(" یا ", parts) + " (به انتخاب شما)";
        return parts[0];
    }

    /// <summary>Gender label. As a listing requirement, Any → "فرقی نمی‌کند".</summary>
    public static string GenderLabel(Gender g, bool asRequirement = true) => g switch
    {
        Gender.Male => "آقا",
        Gender.Female => "خانم",
        _ => asRequirement ? "فرقی نمی‌کند" : "نامشخص",
    };
}
