<?php

namespace goodboyalex\php_components_pack\extensions;

use Random\RandomException;

/**
 * Расширение Guid.
 *
 * @author Александр Бабаев
 * @package php_components_pack
 * @version 1.0
 * @since 1.0
 */
final class GUIDExtension
{
    /**
     * Пустой Guid.
     */
    public const string GUIDEmpty = "00000000-0000-0000-0000-000000000000";

    /**
     * Генерирует Guid.
     *
     * @return string Сгенерированный Guid.
     */
    public static function Generate (): string
    {
        // Цикл создания Guid
        do
            $guid = self::DoGenerate();
            // - пока Guid не будет корректен
        while (!self::Validate($guid));

        // Возвращаем Guid
        return $guid;
    }

    /**
     * Генерирует Guid.
     *
     * @return string Сгенерированный Guid.
     */
    private static function DoGenerate (): string
    {
        try {
            return sprintf(
                '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
                // 32 bits for "time_low"
                random_int(0, 0xffff),
                random_int(0, 0xffff),

                // 16 bits for "time_mid"
                random_int(0, 0xffff),

                // 16 bits for "time_hi_and_version",
                // four most significant bits holds version number 4
                random_int(0, 0x0fff) | 0x4000,

                // 16 bits, 8 bits for "clk_seq_hi_res",
                // 8 bits for "clk_seq_low",
                // two most significant bits holds zero and one for variant DCE1.1
                random_int(0, 0x3fff) | 0x8000,

                // 48 bits for "node"
                random_int(0, 0xffff),
                random_int(0, 0xffff),
                random_int(0, 0xffff)
            );
        }
        catch (RandomException) {
            return self::GUIDEmpty;
        }
    }

    /**
     * Проверяет Guid на корректность.
     *
     * @param string|null $str Guid на проверку
     *
     * @return bool Корректен ли Guid.
     */
    public static function Validate (?string $str): bool
    {
        // Если Guid пустой
        if (StringExtension::IsNullOrWhitespace($str))
            // - возвращаем false
            return false;

        // Проверяем длину
        $isLenCorrect = strlen($str) == 36;

        // Если длина не корректна
        if (!$isLenCorrect)
            // - возвращаем false
            return false;

        // Разбиваем на части
        $explodedStr = explode("-", $str);

        // Если количество частей не равно 5
        if (count($explodedStr) !== 5)
            // - возвращаем false
            return false;

        // Проверяем длину каждой части
        // - первая часть должна быть длиной 8 символов
        if (strlen($explodedStr[0]) !== 8)
            // -- возвращаем false
            return false;

        // - вторая часть должна быть длиной 4 символа
        if (strlen($explodedStr[1]) !== 4)
            // -- возвращаем false
            return false;

        // - третья часть должна быть длиной 4 символа
        if (strlen($explodedStr[2]) !== 4)
            // -- возвращаем false
            return false;

        // - четвертая часть должна быть длиной 4 символа
        if (strlen($explodedStr[3]) !== 4)
            // -- возвращаем false
            return false;

        // - пятая часть должна быть длиной 12 символов
        if (strlen($explodedStr[4]) !== 12)
            // -- возвращаем false
            return false;

        // Проверка пройдена
        return true;
    }

    /**
     * Проверяет Guid на пустоту.
     *
     * @param string|null $str Guid на проверку
     *
     * @return bool Пустой ли GUID
     */
    public static function IsNotValidOrEmpty (?string $str): bool
    {
        return !self::Validate($str) || $str == self::GUIDEmpty;
    }
}