<?php

declare(strict_types=1);

namespace App\Service\Finance;

final class AmountToWords
{
    /** @var array<int, string> */
    private const SMALL = [
        0 => 'zéro',
        1 => 'un',
        2 => 'deux',
        3 => 'trois',
        4 => 'quatre',
        5 => 'cinq',
        6 => 'six',
        7 => 'sept',
        8 => 'huit',
        9 => 'neuf',
        10 => 'dix',
        11 => 'onze',
        12 => 'douze',
        13 => 'treize',
        14 => 'quatorze',
        15 => 'quinze',
        16 => 'seize',
    ];

    /** @var array<int, string> */
    private const TENS = [
        20 => 'vingt',
        30 => 'trente',
        40 => 'quarante',
        50 => 'cinquante',
        60 => 'soixante',
    ];

    public function toFcfa(string|int|float|null $amount): string
    {
        $number = max(0, (int) round((float) ($amount ?? 0)));
        $words = $this->convert($number);

        return mb_strtoupper(mb_substr($words, 0, 1)).mb_substr($words, 1).' FCFA';
    }

    private function convert(int $number): string
    {
        if ($number < 17) {
            return self::SMALL[$number];
        }

        if ($number < 20) {
            return 'dix-'.$this->convert($number - 10);
        }

        if ($number < 100) {
            return $this->convertBelowHundred($number);
        }

        if ($number < 1000) {
            return $this->convertBelowThousand($number);
        }

        if ($number < 1000000) {
            $thousands = intdiv($number, 1000);
            $rest = $number % 1000;
            $prefix = $thousands === 1 ? 'mille' : $this->convert($thousands).' mille';

            return $rest > 0 ? $prefix.' '.$this->convert($rest) : $prefix;
        }

        $millions = intdiv($number, 1000000);
        $rest = $number % 1000000;
        $prefix = $millions === 1 ? 'un million' : $this->convert($millions).' millions';

        return $rest > 0 ? $prefix.' '.$this->convert($rest) : $prefix;
    }

    private function convertBelowHundred(int $number): string
    {
        if ($number < 70) {
            $ten = intdiv($number, 10) * 10;
            $unit = $number % 10;
            if ($unit === 0) {
                return self::TENS[$ten];
            }

            return self::TENS[$ten].($unit === 1 ? ' et ' : '-').$this->convert($unit);
        }

        if ($number < 80) {
            return 'soixante'.($number === 71 ? ' et ' : '-').$this->convert($number - 60);
        }

        if ($number === 80) {
            return 'quatre-vingts';
        }

        return 'quatre-vingt-'.$this->convert($number - 80);
    }

    private function convertBelowThousand(int $number): string
    {
        $hundreds = intdiv($number, 100);
        $rest = $number % 100;

        if ($hundreds === 1) {
            $prefix = 'cent';
        } else {
            $prefix = $this->convert($hundreds).' cent';
        }

        if ($rest === 0) {
            return $hundreds > 1 ? $prefix.'s' : $prefix;
        }

        return $prefix.' '.$this->convert($rest);
    }
}
