<?php

namespace engine\lang;

use ArrayAccess;
use engine\exceptions\NotImplementedException;

/**
 * @implements ArrayAccess<string,string|string[]>
 */
class StringsBase implements ArrayAccess
{
    /** @var array<string,string|string[]> */
    protected array $data = [];

    /**
     * @throws NotImplementedException
     */
    public function offsetSet(mixed $offset, mixed $value): void {
        throw new NotImplementedException();
    }

    public function offsetExists(mixed $offset): bool {
        return isset($this->data[$offset]);
    }

    /**
     * @throws NotImplementedException
     */
    public function offsetUnset(mixed $offset): void {
        throw new NotImplementedException();
    }

    /**
     * @param mixed $offset
     * @return string|string[] 
     */
    public function offsetGet(mixed $offset): mixed {
        return $this->data[$offset] ?? '{'.$offset.'}';
    }

    /**
     * Get string or plural strings.
     * @param string $key Key.
     * @param mixed ...$sprintf_args `sprintf` values. If this argument is
     * supplied, then strings value is treated as `sprintf` format string
     * and additional arguments as `sprintf` values.
     * @return string|string[]
     */
    public function get(string $key, mixed ...$sprintf_args): string|array {
        $val = $this->offsetGet($key);
        if (!empty($sprintf_args)) {
            return sprintf($val, ...$sprintf_args);
        } else {
            return $val;
        }
    }

    /**
     * Pluralize by number.
     * @param string|array{0:string,1:string,2:string,3:string} $key Strings key or plural array.
     * @param int $num Count.
     * @param array{format:bool|(\Closure(int $num):string),format_delim:string} $opts Additional options.
     * @return string
     */
    public function num(string|array $key, int $num, array $opts = []): string {
        $s = is_array($key) ? $key : $this->offsetGet($key);
        /** @var array{format:bool|(\Closure(int $num):string),format_delim:string} */
        $opts = array_merge([
            'format' => true,
            'format_delim' => ' '
        ], $opts);

        $n = $num % 100;
        if ($n > 19)
            $n %= 10;

        if ($n == 1) {
            $word = 0;
        } elseif ($n >= 2 && $n <= 4) {
            $word = 1;
        } elseif ($num == 0 && count($s) == 4) {
            $word = 3;
        } else {
            $word = 2;
        }

        // if zero
        if ($word == 3)
            return $s[3];

        if (is_callable($opts['format'])) {
            /** @var string */
            $num = $opts['format']($num);
        } else if ($opts['format'] === true) {
            $num = formatNumber($num, $opts['format_delim']);
        }

        return sprintf($s[$word], $num);
    }
}
