<?php

declare(strict_types=1);

/**
 * تبدیل عدد صحیح به حروف فارسی
 *
 * پشتیبانی از اعداد مثبت و منفی تا محدوده PHP_INT_MAX.
 */
function numberToPersianWords(int $number): string
{
    if ($number === 0) {
        return 'صفر';
    }

    if ($number < 0) {
        return 'منفی ' . numberToPersianWords(abs($number));
    }

    $ones = [
        'صفر',
        'یک',
        'دو',
        'سه',
        'چهار',
        'پنج',
        'شش',
        'هفت',
        'هشت',
        'نه',
        'ده',
        'یازده',
        'دوازده',
        'سیزده',
        'چهارده',
        'پانزده',
        'شانزده',
        'هفده',
        'هجده',
        'نوزده',
    ];

    $tens = [
        2 => 'بیست',
        3 => 'سی',
        4 => 'چهل',
        5 => 'پنجاه',
        6 => 'شصت',
        7 => 'هفتاد',
        8 => 'هشتاد',
        9 => 'نود',
    ];

    $hundreds = [
        1 => 'صد',
        2 => 'دویست',
        3 => 'سیصد',
        4 => 'چهارصد',
        5 => 'پانصد',
        6 => 'ششصد',
        7 => 'هفتصد',
        8 => 'هشتصد',
        9 => 'نهصد',
    ];

    $scales = [
        0 => '',
        1 => 'هزار',
        2 => 'میلیون',
        3 => 'میلیارد',
        4 => 'تریلیون',
        5 => 'کوادریلیون',
        6 => 'کوینتیلیون',
    ];

    $convertBelowThousand = static function (
        int $number
    ) use ($ones, $tens, $hundreds): string {

        $parts = [];

        $hundred = intdiv($number, 100);
        $remainder = $number % 100;

        if ($hundred > 0) {
            $parts[] = $hundreds[$hundred];
        }

        if ($remainder > 0) {

            if ($remainder < 20) {
                $parts[] = $ones[$remainder];
            } else {

                $ten = intdiv($remainder, 10);
                $one = $remainder % 10;

                $parts[] = $tens[$ten];

                if ($one > 0) {
                    $parts[] = $ones[$one];
                }
            }
        }

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

    $parts = [];
    $group = 0;

    while ($number > 0) {

        $chunk = $number % 1000;

        if ($chunk > 0) {

            $text = $convertBelowThousand($chunk);

            if ($group > 0) {
                $text .= ' ' . $scales[$group];
            }

            array_unshift($parts, $text);
        }

        $number = intdiv($number, 1000);
        $group++;
    }

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

