namespace ROLAC.API.Services.Disbursement;

/// <summary>
/// Converts a monetary amount to the English words used on a check, e.g.
/// 1234.56 → "One Thousand Two Hundred Thirty-Four and 56/100 Dollars".
/// Pure and dependency-free so it is easily unit-tested.
/// </summary>
public static class AmountToWords
{
    private static readonly string[] Ones =
    [
        "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine",
        "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen",
        "Seventeen", "Eighteen", "Nineteen",
    ];

    private static readonly string[] Tens =
    [
        "", "", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety",
    ];

    // index 1.. → 10^(3*index)
    private static readonly string[] Scales = ["", "Thousand", "Million", "Billion", "Trillion"];

    public static string Convert(decimal amount)
    {
        if (amount < 0) amount = 0m;
        // Round half-up to cents.
        amount = Math.Round(amount, 2, MidpointRounding.AwayFromZero);

        var dollars = (long)Math.Floor(amount);
        var cents   = (int)Math.Round((amount - dollars) * 100m, MidpointRounding.AwayFromZero);

        var words = dollars == 0 ? "Zero" : ThreeDigitGroupsToWords(dollars);
        return $"{words} and {cents:00}/100 Dollars";
    }

    private static string ThreeDigitGroupsToWords(long n)
    {
        // Split into groups of three digits, low to high.
        var groups = new List<int>();
        while (n > 0) { groups.Add((int)(n % 1000)); n /= 1000; }

        var parts = new List<string>();
        for (var i = groups.Count - 1; i >= 0; i--)
        {
            if (groups[i] == 0) continue;
            var group = HundredsToWords(groups[i]);
            var scale = Scales[i];
            parts.Add(string.IsNullOrEmpty(scale) ? group : $"{group} {scale}");
        }
        return string.Join(" ", parts);
    }

    private static string HundredsToWords(int n)
    {
        var parts = new List<string>();
        if (n >= 100)
        {
            parts.Add($"{Ones[n / 100]} Hundred");
            n %= 100;
        }
        if (n >= 20)
        {
            var t = Tens[n / 10];
            var o = n % 10;
            parts.Add(o == 0 ? t : $"{t}-{Ones[o]}");
        }
        else if (n > 0)
        {
            parts.Add(Ones[n]);
        }
        return string.Join(" ", parts);
    }
}
