<?php

namespace app\components;

use yii\base\Component;

class NumberToText extends Component
{
    private array $units = [
        ['нуль'],
        ['один', 'одна'],
        ['два', 'дві'],
        ['три'],
        ['чотири'],
        ['п’ять'],
        ['шість'],
        ['сім'],
        ['вісім'],
        ['дев’ять'],
    ];

    private array $teens = [
        'десять','одинадцять','дванадцять','тринадцять',
        'чотирнадцять','п’ятнадцять','шістнадцять',
        'сімнадцять','вісімнадцять','дев’ятнадцять'
    ];

    private array $tens = [
        '', '', 'двадцять','тридцять','сорок',
        'п’ятдесят','шістдесят','сімдесят',
        'вісімдесят','дев’яносто'
    ];

    private array $hundreds = [
        'сто','двісті','триста','чотириста',
        'п’ятсот','шістсот','сімсот',
        'вісімсот','дев’ятсот'
    ];

    private array $orders = [
        [ ['тисяча','тисячі','тисяч'], 'f'],
        [ ['мільйон','мільйони','мільйонів'], 'm'],
        [ ['мільярд','мільярди','мільярдів'], 'm'],
    ];

    public function money(float $amount): string
    {
        $amount = round($amount, 2);

        $hryvnias = floor($amount);
        $kopecks = (int)round(($amount - $hryvnias) * 100);

        $result  = $this->convert($hryvnias, ['гривня','гривні','гривень'], 'f');
        $result .= ' ';
        $result .= $this->convert($kopecks, ['копійка','копійки','копійок'], 'f');

        return $result;
    }

    private function convert(int $number, array $forms, string $gender): string
    {
        if ($number === 0) {
            return 'нуль ' . $forms[2];
        }

        $result = [];
        $order = 0;

        while ($number > 0) {
            $chunk = $number % 1000;

            if ($chunk > 0) {
                $chunkGender = $order === 0 ? $gender : $this->orders[$order-1][1];
                $words = $this->convertChunk($chunk, $chunkGender);
                $plural = $this->pluralForm($chunk);

                if ($order === 0) {
                    $words[] = $forms[$plural];
                } else {
                    $words[] = $this->orders[$order-1][0][$plural];
                }

                $result = array_merge($words, $result);
            }

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

        return implode(' ', $result);
    }

    private function convertChunk(int $number, string $gender): array
    {
        $words = [];

        if ($number >= 100) {
            $words[] = $this->hundreds[intdiv($number,100)-1];
            $number %= 100;
        }

        if ($number >= 10 && $number <= 19) {
            $words[] = $this->teens[$number-10];
            return $words;
        }

        if ($number >= 20) {
            $words[] = $this->tens[intdiv($number,10)];
            $number %= 10;
        }

        if ($number > 0) {
            if ($number == 1 || $number == 2) {
                $words[] = $this->units[$number][$gender === 'f' ? 1 : 0];
            } else {
                $words[] = $this->units[$number][0];
            }
        }

        return $words;
    }

    private function pluralForm(int $number): int
    {
        $number %= 100;
        if ($number >= 11 && $number <= 19) return 2;

        $number %= 10;
        if ($number == 1) return 0;
        if ($number >= 2 && $number <= 4) return 1;

        return 2;
    }

    public function moneyFormatted(float $amount): string
    {
        $amount = round($amount, 2);

        $hryvnias = floor($amount);
        $kopecks = (int)round(($amount - $hryvnias) * 100);

        $numberFormatted = number_format($amount, 2, ',', ' ');

        $textHryvnias = $this->convert(
            $hryvnias,
            ['','',''],
            'f'
        );

        $plural = $this->pluralForm($hryvnias);
        $forms = ['гривня','гривні','гривень'];

        return sprintf(
            "%s (%s) %s %02d копійок",
            $numberFormatted,
            $textHryvnias,
            $forms[$plural],
            $kopecks
        );
    }
}