<?php declare(strict_types=1);

namespace App\Captcha\Types;

use App\Captcha\Dto\Symbols;
use App\Captcha\Enums\SymbolType;
use App\Captcha\Exceptions\CaptchaException;
use App\Captcha\Type;
use Illuminate\Support\Arr;

final class StringType extends Type
{

    public function getSymbols(): Symbols
    {
        $lengthSymbols = $this->lengthSymbols();
        $lengthFakeSymbols = $this->lengthFakeSymbols();

        $success = $this->randomSymbols($lengthSymbols);
        $fakes = [];
        if (!empty($lengthFakeSymbols)) {
            $fakes = $this->randomSymbols($lengthFakeSymbols, $success);
        }

        return new Symbols(
            success: $success,
            fakes: $fakes,
            type: SymbolType::String
        );
    }

    /**
     * @param int $min
     * @param int $max
     * @param array $except
     * @return array
     * @throws CaptchaException
     */
    private function randomSymbols(int $lenght, array $except = []): array
    {
        $symbols = $this->symbols();
        if (!empty($except)) {
            $symbols = array_diff($symbols, $except);
        }

        if (count($symbols) < $lenght) {
            throw new CaptchaException('The number of characters is less than $lenght.');
        }

        return Arr::random($symbols, $lenght);
    }

    /**
     * @return array
     * @throws CaptchaException
     */
    private function symbols(): array
    {
        $symbols = $this->getConfigValue('symbols');
        if (!is_array($symbols)) {
            throw new CaptchaException('The symbols parameter is not an array.');
        }
        return $symbols;
    }

    private function lengthSymbols(): int
    {
        $length = $this->getConfigValue('length_symbols');
        if (is_integer($length)) {
            return $length;
        }
        if (!is_array($length)) {
            throw new CaptchaException('The length_symbols parameter is not an array or integer.');
        }
        if (count($length) > 2) {
            throw new CaptchaException('The array of length_symbols parameters contains more than 2 keys.');
        }
        if ($length[0] > $length[1]) {
            throw new CaptchaException('The number of length_symbols[1] is less than length_symbols[0].');
        }
        return random_int($length[0], $length[1]);
    }

    private function lengthFakeSymbols(): int
    {
        $length = $this->getConfigValue('length_fake_symbols');
        if (is_integer($length)) {
            return $length;
        }
        if (is_null($length) || $length === false) {
            return 0;
        }
        if (!is_array($length)) {
            throw new CaptchaException('The length_fake_symbols parameter is not an array or integer or null or false.');
        }
        if (count($length) > 2) {
            throw new CaptchaException('The array of length_fake_symbols parameters contains more than 2 keys.');
        }
        if ($length[0] > $length[1]) {
            throw new CaptchaException('The number of length_fake_symbols[1] is less than length_fake_symbols[0].');
        }
        return random_int($length[0], $length[1]);
    }
}
