<?php

declare(strict_types=1);

namespace app\models;

use yii;
use yii\web\IdentityInterface;

use carono\exchange1c\interfaces\PartnerInterface;

use app\models\Dellin;
use app\models\traits\SearchByEdge;

use yii\web\UploadedFile;

/**
 * Аккаунт
 *
 * Реализует аккаунты пользователей и поставщиков
 */
class Account extends Document implements IdentityInterface, PartnerInterface
{
    use SearchByEdge;

    /**
     * Имя коллекции
     */
    public static function collectionName(): string
    {
        return 'account';
    }

    /**
     * Свойства
     */
    public function attributes(): array
    {
        return array_merge(
            parent::attributes(),
            [
                'name',
                'mail',
                'desc',
                'indx',
                'pswd',
                'simc',
                'boss',
                'sity',
                'zone',
                'comp',
                'cntc',
                'taxn',
                'cntg',
                'fadd',
                'ladd',
                'chck',
                'onec',
                'opts',
                'agnt',
                'type',
                'vrfy',
                'geol',
                'auth',
                'acpt',
                'chpk'
            ]
        );
    }

    /**
     * Метки свойств
     */
    public function attributeLabels(): array
    {
        return array_merge(
            parent::attributeLabels(),
            [
                'name' => 'Контактное лицо',
                'mail' => 'Почта',
                'desc' => 'Описание',
                'indx' => 'Псевдоанонимный идентификатор',
                'pswd' => 'Пароль',
                'simc' => 'Номер',
                'boss' => 'Директор',
                'sity' => 'Город',
                'zone' => 'Часовой пояс',
                'comp' => 'Компания',
                'cntc' => 'Контакты',
                'taxn' => 'ИНН',
                'cntg' => 'КПП',
                'fadd' => 'Фактический адрес',
                'ladd' => 'Юридический адрес',
                'chck' => 'Расчетный счет',
                'onec' => 'Данные 1C',
                'opts' => 'Параметры',
                'agnt' => 'Агент (поставщик)',
                'type' => 'Тип аккаунта',
                'vrfy' => 'Статус подтверждения владением почты',
                'geol' => 'Геолокация',
                'auth' => 'Аутентификационный хеш',
                'acpt' => 'Согласие с офертой',
                'chpk' => 'Ключ для смены пароля'
            ]
        );
    }

    /**
     * Правила
     */
    public function rules(): array
    {
        return array_merge(
            parent::rules(),
            [
                [
                    [
                        'mail',
                        'pswd'
                    ],
                    'required',
                    'message' => 'Заполните поле'
                ],
                [
                    'mail',
                    'email'
                ],
                [
                    [
                        'mail',
                        'indx',
                        'comp',
                        'simc'
                    ],
                    'unique',
                    'message' => 'Атрибут {attribute} должен иметь уникальное значение'
                ],
                [
                    [
                        'indx',
                        'zone'
                    ],
                    'string'
                ],
                [
                    'vrfy',
                    'validateVrfyUnique'
                ],
                [
                    'taxn',
                    'integer',
                    'min' => 0,
                    'max' => 999999999999
                ],
                [
                    'cntg',
                    'integer',
                    'min' => 0,
                    'max' => 99999999999999999999
                ],
                [
                    'ladd',
                    'string',
                    'length' => [0, 200]
                ],
                [
                    [
                        'desc',
                        'cntc'
                    ],
                    'string',
                    'length' => [0, 500]
                ],
                [
                    'simc',
                    'integer',
                    'min' => 0,
                    'max' => 999999999999999999999999999999
                ],
                [
                    'comp',
                    'string',
                    'length' => [0, 40]
                ],
                [
                    [
                        'name',
                        'boss'
                    ],
                    'string',
                    'length' => [0, 100]
                ]
            ]
        );
    }

    /**
     * Перед сохранением
     *
     * @todo Подождать обновление от ебаного Yii2 и добавить
     * проверку типов передаваемых параметров
     */
    public function beforeSave($data): bool
    {
        if (parent::beforeSave($data)) {
            if ($this->isNewRecord) {
                $this->mail = mb_strtolower($this->mail);
                $this->auth = yii::$app->security->generateRandomString();
                $this->vrfy = yii::$app->security->generateRandomString();
            }

            return true;
        }
        return false;
    }

    /**
     * Отправка сообщения для генерации нового пароля
     */
    public static function restoreSend(string $mail): bool
    {
        if (($account = self::findByMail($mail)) instanceof self) {
            // Найден аккаунт

            // Запись ключа для аутентификации
            $account->chpk = yii::$app->security->generateRandomString();

            if ($account->update()) {
                // Удалось обновить аккаунт

                // Отправка письма
                yii::$app->mail_system->compose()
                    ->setFrom(yii::$app->params['mail']['system'])
                    ->setTo($account->mail)
                    ->setSubject('Подтвердите сброс пароля')
                    ->setHtmlBody(yii::$app->controller->renderPartial('/mails/restore', ['id' => $account->_key, 'chpk' => $account->chpk]))
                    ->send();

                return true;
            }
        }

        return false;
    }

    /**
     * Генерация нового пароля
     */
    public function restoreGenerate(): void
    {
        // Удаление ключа
        $this->chpk = null;

        // Генерация пароля
        $this->pswd = self::passwordGenerate();

        if ($this->update()) {
            // Удалось обновить аккаунт

            // Отправка письма
            yii::$app->mail_system->compose()
                ->setFrom(yii::$app->params['mail']['system'])
                ->setTo($this->mail)
                ->setSubject('Генерация пароля')
                ->setHtmlBody(yii::$app->controller->renderPartial('/mails/password', ['pswd' => $this->pswd]))
                ->send();
        }
    }

    /**
     * Чтение полей для экспорта из 1С
     */
    public function getExportFields1c($context = null): array
    {
        return [];
    }

    /**
     * Чтение идентификатора
     *
     * @see IdentityInterface
     */
    public function getId(): string
    {
        return self::collectionName() . '/' . $this->_key;
    }

    /**
     * Чтение идентификатора
     */
    public function readId(): string
    {
        return $this->getId();
    }

    /**
     * Чтение аутентификационного ключа
     */
    public function getAuthKey(): string
    {
        return $this->auth;
    }

    /**
     * Идентификация
     *
     * @todo Подождать обновление от ебаного Yii2 и добавить
     * проверку типов передаваемых параметров
     */
    public static function findIdentity($_id): self
    {
        return static::findById($_id);
    }

    /**
     * Поиск по ключу
     *
     * @todo Подождать обновление от ебаного Yii2 и добавить
     * проверку типов передаваемых параметров
     */
    public static function findIdentityByAccessToken($pass, $type = null): self
    {
        return static::findOne(['pass' => $pass]);
    }

    /**
     * Поиск по почте
     *
     * @todo Подождать обновление Yii2 и добавить
     * проверку типов передаваемых параметров
     */
    public static function findByMail($mail): ?self
    {
        return static::findOne(['mail' => $mail]);
    }

    /**
     * Поиск по статусу активации почты
     *
     * @todo Подождать обновление Yii2 и добавить
     * проверку типов передаваемых параметров
     */
    public static function findByVrfy($vrfy): ?self
    {
        return static::findOne(['vrfy' => $vrfy]);
    }

    /**
     * Поиск по идентификатору
     *
     * @todo Подождать обновление Yii2 и добавить
     * проверку типов передаваемых параметров
     */
    public static function findById($_id): self
    {
        return static::searchById($_id);
    }

    /**
     * Чтение всех модераторов
     */
    public static function readAllModerators(): array
    {
        return static::find()->where(['type' => 'moderator'])->all();
    }

    /**
     * Чтение всех администраторов
     */
    public static function readAllAdministrators(): array
    {
        return static::find()->where(['type' => 'administrator'])->all();
    }

    /**
     * Чтение всех уполномоченных аккаунтов
     */
    public static function readAllAuthorizeds(): array
    {
        return static::find()->where(['type' => 'moderator'])->orWhere(['type' => 'administrator'])->all();
    }

    /**
     * Подтверждение регистрации
     */
    public static function verification(string $vrfy, bool $auth = false): bool
    {
        if ($account = static::findByVrfy($vrfy)) {
            // Аккаунт найден

            // Запись в буфер
            $account->vrfy = true;

            // Отправка изменений
            $updated = $account->update() > 0;

            if ($updated && $auth) {
                // Регистрация была подтверждена, а так же запрошена автоматическая аутентификация

                // Аутентификация
                yii::$app->user->login($account, true ? 3600 * 24 * 30 : 0);
            }

            return $updated;
        }

        return false;
    }

    /**
     * Проверка пароля с хешированием
     */
    public function validatePasswordWithHash(string $pswd): bool
    {
        return yii::$app->security->validatePassword($pswd, $this->pswd);
    }

    /**
     * Проверка пароля без хеширования
     */
    public function validatePasswordWithoutHash(string $pswd): bool
    {
        return $pswd === $this->pswd;
    }

    /**
     * Проверка аутентификационного ключа
     *
     * @todo Подождать обновление Yii2 и добавить
     * проверку типов передаваемых параметров
     */
    public function validateAuthKey($auth): bool
    {
        return $this->getAuthKey() === $auth;
    }

    /**
     * Проверка почты
     */
    public static function validateMail(string $mail): bool
    {
        if ($account = static::findByMail($mail)) {
            // Почта найдена в базе данных

            if (isset($account) && $account->vrfy === true) {
                // Регистрация подтверждена

                return true;
            }
        }

        return false;
    }

    /**
     * Проверка статуса подтверждения регистрации
     */
    public static function validateVrfy(string $vrfy): bool
    {
        if ($account = static::findByVrfy($vrfy)) {
            // Статус найден в базе данных

            if ($account->vrfy === true) {
                // Регистрация подтверждена

                return true;
            }
        }

        return false;
    }

    /**
     * Проверка данных для поля подтверждения регистрации
     */
    public function validateVrfyUnique($attribute, $params)
    {
        if (!$this->hasErrors()) {
            // Проблем нет

            if ($attribute !== true && $this->validateVrfy($attribute)) {
                // Проверка не пройдена

                $this->addError($attribute, 'Ключ подтверждения регистрации уже используется');
            }
        }
    }

    /**
     * Записать параметр
     *
     * @param string $name Название параметра
     * @param mixed $value Значение параметра
     */
    public function writeOption(string $name, mixed $value = null): bool
    {
        // Запись
        $this->opts = [$name => $value] + $this->opts;

        // Отправка
        return $this->update() > 0;
    }

    /**
     * Удалить параметр
     *
     * @param string $name Название параметра
     */
    public function deleteOption(string $name): bool
    {
        // Удаление
        unset($this->opts[$name]);

        // Отправка
        return $this->update() > 0;
    }

    /**
     * Генерация списка городов
     *
     * Актуальное (выбранное, активное) значение записывается первым
     */
    public function genListCity(): void
    {
        // $browser = new Dellin();

        // $request = $browser->get('https://api.github.com/user', [
        //     'auth' => ['user', 'pass']
        // ]);
    }

    /**
     * Генерация списка OEM-номеров
     *
     * Актуальное (выбранное, активное) значение записывается первым
     *
     * @param array $supplies Необработанный список поставок
     */
    public function genListOem(array $supplies): array
    {
        // Инициализация
        $list = [];

        foreach ($supplies as $supply) {
            // Перебор поставок
            if (in_array($supply['ЗначенияСвойства']['Ид'], $list, true)) {
                // Если встретился дубликат (исполняется очень часто)

                continue;
            }

            // Генерация
            empty($supply['ЗначенияСвойства']['Наименование']) or $list[$supply['ЗначенияСвойства']['Ид']] = $supply['ЗначенияСвойства']['Наименование'];
        }

        return $this->syncListWithSettings($list, 'import_supplies_oem');
    }

    /**
     * Генерация списка терминалов из ДеловыеЛинии для получателя
     *
     * Актуальное (выбранное, активное) значение записывается первым
     *
     * @param array Необработанный список терминалов
     */
    public function genListTerminalsTo(): array
    {
        // Инициализация
        $list = [];

        $terminals = Terminal::readAll();

        foreach ($terminals as $terminal) {
            // Перебор городов

            if (in_array($terminal->dell, $list, true)) {
                // Если встретился дубликат (исполняется очень часто)

                continue;
            }

            // Запись
            empty($terminal->city) && empty($terminal->strt) && empty($terminal->hous)
                or $list[$terminal->dell] = (empty($terminal->city) ? '' : "г. $terminal->city") . (empty($terminal->strt) ? '' : ", ул. $terminal->strt") . (empty($terminal->hous) ? '' : ", д. $terminal->hous") . (empty($terminal->offs) ? '' : ", оф. $terminal->offs") . (empty($terminal->comm) ? '' : " ($terminal->comm)");
        }

        return $this->syncListWithSettings($list, 'delivery_to_terminal');
    }

    /**
     * Синхронизация списка вариантов параметра с текущим значением из настроек
     *
     * @param array &$list Список
     * @param string $var Название параметра
     *
     * @return array Сортированный список
     */
    protected function syncListWithSettings(array &$list, string $var): array
    {
        // Инициализация текущего значения параметра в начале массива
        if (isset($this->opts[$var])) {
            // Параметр найден в настройках аккаунта

            if (isset($list[$this->opts[$var]])) {
                // Найдено совпадение сохранённого параметра с полученным списком из поставок

                // Буфер для сохранения параметра
                $buffer = $list[$this->opts[$var]];

                // Удаление параметра
                unset($list[$this->opts[$var]]);

                // Сохранение параметра в начале массива
                $list = [$this->opts[$var] => $buffer] + $list;
            } else {
                // Совпадение не найдено

                // Сохранение параметра из данных аккаунта в начале массива
                $list = [$this->opts[$var] => $this->opts[$var]] + $list;
            }
        } else {
            // Параметр $var не найден в настройках аккаунта

            // Сохранение параметра из данных аккаунта в начале массива
            $list = ['Выберите' => 'Выберите'] + $list;
        }

        return $list;
    }

    /**
     * Генерация псевдоанонимных индексов
     *
     * @param [int] $accounts Массив аккаунтов
     * @param bool $force Перезаписывать, если существует
     *
     * @return int Количество успешно обработанных аккаунтов
     */
    public static function generateIndexes(array $accounts, bool $force = false): int
    {
        // Инициализация
        $amount = 0;

        // Функция для конвертации цифр в буквы
        $int_to_string = function (int $target): string {
            // $alphabet = ['А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ё', 'Ж', 'З', 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Ъ', 'Ы', 'Ь', 'Э', 'Ю', 'Я'];
            $alphabet = ['А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ё', 'Ж', 'З', 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Ю', 'Я'];

            return $alphabet[$target];
        };

        // Реестр запрещённых индексов
        $registry = [
            'ЧМО',
            'ХЕР',
            'ХУЙ',
            'БЛЯ',
            'СУК',
            'СУЧ',
            'ЖОП',
            'ЛОХ'
        ];

        foreach ($accounts as $account) {
            // Перебор запрошенных аккаунтов

            if ($force || empty($account->indx)) {
                // Запись только тех аккаунтов у кого не инициализирован индекс, либо если указана перезапись

                // Повтор генерации
                regenerate_index:

                // Генерация
                $account->indx = $int_to_string(random_int(0, 27)) . $int_to_string(random_int(0, 27)) . $int_to_string(random_int(0, 27));

                if (in_array($account->indx, $registry)) {
                    // Сгенерированный индекс обнаружено в реестре запрещённых индексов

                    // Повтор генерации
                    goto regenerate_index;
                }

                // Запись
                if ($account->save()) {
                    // Аккаунт сохранён (в базе данных это поле проверяется на уникальность)

                    if (yii::$app->getRequest()->isConsoleRequest) {
                        // Вызов из терминала

                        echo "Удалось сохранить аккаунт с псевдоанонимным идентификатором $account->indx" . PHP_EOL;
                    }

                    // Запись операции в счётчик
                    $amount++;
                } else {
                    // Аккаунт не сохранен (подразумевается несовпадение идентификаторов)

                    if (yii::$app->getRequest()->isConsoleRequest) {
                        // Вызов из терминала

                        echo "Не удалось сохранить аккаунт с псевдоанонимным идентификатором $account->indx" . PHP_EOL;
                    }

                    goto regenerate_index;
                }
            }
        }

        return $amount;
    }

    /**
     * Найти по идентификатору поставки
     *
     * @param string|null $_id Идентификатор поставки
     *
     * @return self|null Аккаунт владельца
     */
    public static function searchBySupplyId(string $_id): ?array
    {
        return static::searchByEdge(
            from: 'supply',
            to: 'account',
            edge: 'account_edge_supply',
            direction: 'OUTBOUND',
            subquery_where: [
                [
                    'account_edge_supply._to == "' . $_id . '"'
                ]
            ],
            subquery_select: 'account',
            where: 'account_edge_supply[0]._id != null',
            select: 'account_edge_supply[0]',
            limit: 1
        )[0] ?? null;
    }

    /**
     * Генерация текста обозначающего статус аккаунта на русском языке
     *
     * @return string Статус аккаунта
     */
    public function statusLabel(): string
    {
        return match ($this->type) {
            'administrator', 'admin' => 'Администратор',
            'moderator', 'moder' => 'Модератор',
            'supplier' => 'Поставщик',
            default => 'Покупатель',
        };
    }

    public function sendMailVerify(): bool
    {
        if (isset($this->mail, $this->vrfy) && !$this->validateVrfy($this->vrfy)) {
            // Данные инициализированы, а регистрация ещё не была подтверждена

            // Отправка письма
            yii::$app->mail_system->compose()
                ->setFrom(yii::$app->params['mail']['system'])
                ->setTo($this->mail)
                ->setSubject('Подтверждение регистрации')
                ->setHtmlBody(yii::$app->controller->renderPartial('/mails/verify', ['vrfy' => $this->vrfy, 'password' => $this->pswd]))
                ->send();
        }

        return false;
    }

    public function sendMailDecline(string $reason = 'Проблема с данными'): bool
    {
        // Отправка письма
        yii::$app->mail_system->compose()
            ->setFrom(yii::$app->params['mail']['system'])
            ->setTo($this->mail)
            ->setSubject('Отказано в регистрации')
            ->setHtmlBody(yii::$app->controller->renderPartial('/mails/suppliers/decline', ['reason' => $reason]))
            ->send();

        return false;
    }

    /**
     * Инициализация пароля
     */
    public function passwordInit(): bool
    {
        if (empty($this->pswd) || $this->pswd === 'undefined') {
            // Пароль не инициализирован

            // Генерация пароля
            $this->pswd = self::passwordGenerate();

            // Возврат
            return true;
        }

        // Возврат (подразумевается неудача)
        return false;
    }

    /**
     * Гениальный генератор пароля
     *
     * 35 * 19 * 9 = 5985 комбинаций
     *
     * @return string|null
     */
    public static function passwordGenerate(): ?string
    {
        $password = '';

        $arr = array(
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
            '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'
        );

        for ($i = 0; $i < 6; $i++) {
            $password .= $arr[random_int(0, count($arr) - 1)];
        }

        return $password;
        // return match (rand(1, 35)) {
        //     1 => 'салазки',
        //     2 => 'запчасти',
        //     3 => 'инструменты',
        //     4 => 'детали',
        //     5 => 'компоненты',
        //     6 => 'ремни',
        //     7 => 'шестерни',
        //     8 => 'блоки',
        //     9 => 'коронки',
        //     10 => 'вал',
        //     11 => 'пыльник',
        //     12 => 'шкив',
        //     13 => 'станок',
        //     14 => 'сальник',
        //     15 => 'кольцо',
        //     16 => 'цепь',
        //     17 => 'редуктор',
        //     18 => 'фильтр',
        //     19 => 'клапан',
        //     20 => 'фару',
        //     21 => 'мотор',
        //     22 => 'подшипник',
        //     23 => 'болт',
        //     24 => 'стартер',
        //     25 => 'двигатель',
        //     26 => 'трубку',
        //     27 => 'прокладку',
        //     28 => 'помпу',
        //     29 => 'запчасть',
        //     30 => 'втулку',
        //     31 => 'уплотнение',
        //     32 => 'ролик',
        //     33 => 'датчик',
        //     34 => 'насос',
        //     default => 'машину'
        // }
        //     . ' ' . match (rand(1, 15)) {
        //         1 => 'забыли',
        //         2 => 'испортили',
        //         3 => 'забрали',
        //         4 => 'порвали',
        //         5 => 'украли',
        //         6 => 'обменяли',
        //         7 => 'угнали',
        //         8 => 'взорвали',
        //         9 => 'поломали',
        //         1 => 'доломали',
        //         11 => 'утопили',
        //         12 => 'испортили',
        //         13 => 'добили',
        //         14 => 'разбили',
        //         15 => 'сорвали',
        //         default => 'сломали'
        //     }
        //     . ' ' . match (rand(1, 9)) {
        //         1 => 'закажу',
        //         2 => 'найду',
        //         3 => 'отыщу',
        //         4 => 'запрошу',
        //         5 => 'поищу',
        //         6 => 'оформлю',
        //         7 => 'заменю',
        //         8 => 'поменяю',
        //         default => 'куплю'
        //     }
        //     . ' в скиллпартс';
    }

    /**
     * Пересоздать ключ подтверждения регистрации и пароль к аккаунту
     *
     * @return bool
     */
    public function verifyRegenerate(): bool
    {
        $this->pswd = static::passwordGenerate();
        $this->vrfy = yii::$app->security->generateRandomString();

        return $this->update() > 0 ? true : false;
    }

    /**
     * Запись поставщика
     *
     * @return array
     */
    public static function writeSupplier(string $name, string $phone, string $mail, UploadedFile $file): ?static
    {
        // Инициализация
        $account = new static;

        // Настройка
        $account->agnt = true;
        $account->name = $name;
        $account->boss = $name;
        $account->simc = $phone;
        $account->mail = $mail;
        $account->type = 'requested';

        // Генерация пароля
        $account->passwordInit();

        if ($account->save()) {
            // Удалось сохранить аккаунт

            // Инициализация индекса
            Account::generateIndexes([$account]);

            if (!file_exists($path = YII_PATH_PUBLIC . "/../assets/accounts/$account->_key/files")) {
                // Директория для хранения документов не найдена

                if (!mkdir($path, 0775, true)) {
                    // Не удалось записать директорию

                    return false;
                }
            }

            // Перенос файла из временной директории
            copy($file->tempName, $path .= "/$file->name");

            // Запись в журнал
            $account->journal('request to become a supplier', [
                'name' => $name,
                'phone' => $phone,
                'mail' => $mail,
                'file' => [
                    'path' => $path,
                    'uri' => "/$account->_key/files/$file->name"
                ]
            ]);

            // Отправка уведомлений
            Notification::_write(yii::$app->controller->renderPartial('/notification/system/suppliers/request', ['_key' => $account->_key]), html: true, account: '@authorized');

            return $account;
        }

        return null;
    }

    /**
     * Поиск заявок на регистрацию поставщиков
     *
     * @return array
     */
    public static function searchSuppliersRequests(int $amount = 3, int $page, array $order = ['DESC']): array
    {
        return self::find()->where(['agnt' => true, 'type' => 'requested'])->limit($amount)->offset($amount * ($page - 1))->orderBy($order)->all();
    }

    /**
     * Инициализация аккаунта для контроллера
     *
     * @param static|null $account Аккаунт
     */
    public static function initAccount(Account|string|int $account = null): ?static
    {
        if (is_null($account)) {
            // Данные аккаунта не переданы

            if (empty(yii::$app->user) || yii::$app->user->isGuest) {
                // Аккаунт не аутентифицирован
            } else {
                // Аккаунт аутентифицирован

                // Инициализация
                $account = yii::$app->user->identity;

                return $account;
            }
        } else {
            if ($account instanceof Account) {
                // Передана инстанция аккаунта

                return $account;
            } else if (is_int($account)) {
                // Передан идентификатор (_key) аккаунта (подразумевается)

                // Инициализация (поиск в базе данных)
                if ($account = Account::searchById(Account::collectionName() . "/$account")) {
                    // Удалось инициализировать аккаунт

                    return $account;
                }
            } else if (is_string($account)) {
                // Передан идентификатор документа (_id) (подразумевается)

                // Инициализация (поиск в базе данных)
                if ($account = Account::searchById($account)) {
                    // Удалось инициализировать аккаунт

                    return $account;
                }
            }
        }

        return null;
    }

    /**
     * Проверка на то, что аккаунт является администраторрм
     *
     * @param static|null $account Аккаунт
     */
    public static function isAdmin($account = null): bool
    {
        if ($account = self::initAccount($account)) {
            // Аккаунт инициализирован

            if ($account->type === 'administrator') {
                return true;
            }
        }

        return false;
    }

    /**
     * Проверка на то, что аккаунт является модератором
     *
     * @param static|null $account Аккаунт
     */
    public static function isModer($account = null): bool
    {
        if ($account = self::initAccount($account)) {
            // Инициализирован аккаунт

            if ($account->type === 'moderator') {
                return true;
            }
        }

        return false;
    }

    /**
     * Проверка на то, что аккаунт имеет минимальный доступ
     *
     * @param static|null $account Аккаунт
     */
    public static function isMinimalAuthorized($account = null): bool
    {
        return self::isModer($account) || self::isAdmin($account);
    }

    /**
     * Сгенерировать тип аккаунта на русском языке
     */
    public function type(): string
    {
        return match ($this->type) {
            'administrator' => 'Администратор',
            'moderator' => 'Модератор',
            'user' => 'Пользователь',
            'requested' => 'Запрошен',
            default => 'Неизвестно'
        };
    }

    public static function filled($account = null): bool|self
    {
        if ($account = self::initAccount($account)) {
            // Инициализирован аккаунт

            // Проверка на заполненность обязательных полей
            if (empty($account->name)) $account->addError('name', 'Не заполнено необходимое поле для заказа');
            if (empty($account->boss)) $account->addError('boss', 'Не заполнено необходимое поле для заказа');
            if (empty($account->simc)) $account->addError('simc', 'Не заполнено необходимое поле для заказа');
            if (empty($account->comp)) $account->addError('comp', 'Не заполнено необходимое поле для заказа');
            if (empty($account->mail)) $account->addError('mail', 'Не заполнено необходимое поле для заказа');
            if (empty($account->taxn)) $account->addError('taxn', 'Не заполнено необходимое поле для заказа');
            if (empty($account->cntg)) $account->addError('cntg', 'Не заполнено необходимое поле для заказа');
            if (empty($account->fadd)) $account->addError('fadd', 'Не заполнено необходимое поле для заказа');
            if (empty($account->ladd)) $account->addError('ladd', 'Не заполнено необходимое поле для заказа');

            return $account->hasErrors() ? $account : true;
        }

        return false;
    }
}
