<?php

namespace App\Support;

/**
 * Converts money amounts to English words for official documents (payroll
 * journal, payment vouchers). Renders Naira and Kobo, e.g.
 * 1,069,501.00 → "One Million Sixty Nine Thousand Five Hundred and One Naira Only".
 */
class AmountInWords
{
    private const ONES = [
        '', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine',
        'Ten', 'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen',
        'Seventeen', 'Eighteen', 'Nineteen',
    ];

    private const TENS = [
        '', '', 'Twenty', 'Thirty', 'Forty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninety',
    ];

    private const SCALES = ['', 'Thousand', 'Million', 'Billion', 'Trillion'];

    public static function naira(float $amount): string
    {
        $amount = round($amount, 2);
        $naira = (int) floor($amount);
        $kobo = (int) round(($amount - $naira) * 100);

        $words = self::words($naira).' Naira';
        if ($kobo > 0) {
            $words .= ' '.self::words($kobo).' Kobo';
        } else {
            $words .= ' Only';
        }

        return $words;
    }

    /** Whole-number to words. */
    public static function words(int $number): string
    {
        if ($number === 0) {
            return 'Zero';
        }
        if ($number < 0) {
            return 'Minus '.self::words(-$number);
        }

        // Split into 3-digit groups, least significant first.
        $groups = [];
        while ($number > 0) {
            $groups[] = $number % 1000;
            $number = intdiv($number, 1000);
        }

        $parts = [];
        for ($i = count($groups) - 1; $i >= 0; $i--) {
            if ($groups[$i] === 0) {
                continue;
            }
            $chunk = self::threeDigits($groups[$i]);
            $scale = self::SCALES[$i] ?? '';
            $parts[] = trim($chunk.' '.$scale);
        }

        return trim(implode(' ', $parts));
    }

    private static function threeDigits(int $n): string
    {
        $out = '';
        $hundreds = intdiv($n, 100);
        $rest = $n % 100;

        if ($hundreds > 0) {
            $out .= self::ONES[$hundreds].' Hundred';
            if ($rest > 0) {
                $out .= ' and ';
            }
        }

        if ($rest > 0) {
            if ($rest < 20) {
                $out .= self::ONES[$rest];
            } else {
                $out .= self::TENS[intdiv($rest, 10)];
                if ($rest % 10 > 0) {
                    $out .= ' '.self::ONES[$rest % 10];
                }
            }
        }

        return $out;
    }
}
