<?php

declare(strict_types=1);

namespace app\models;

use yii;

use app\models\traits\SearchByEdge;

use app\models\Account;

use Exception;

/**
 * Поиск
 *
 * @see Product Поиск по товарам
 *
 * @todo Нет смысла каждый раз возвращать кнопку, надо написать проверки
 */
class Notification extends Document
{
    use SearchByEdge;

    /**
     * Сценарий для доверенного пользователя с созданием уведомления
     */
    const SCENARIO_TRUSTED_CREATE = 'create';

    /**
     * Уведомление: "памятка"
     */
    const TYPE_NOTICE = 'notice';

    /**
     * Уведомление: "предупреждение"
     */
    const TYPE_WARNING = 'warning';

    /**
     * Уведомление: "ошибка"
     */
    const TYPE_ERROR = 'error';

    /**
     * Уведомление для модераторов: "новый заказ"
     */
    const TYPE_MODERATOR_ORDER_NEW = 'new order';

    /**
     * Цель для отправки уведомления
     *
     * Расшифровывается как $target
     *
     * @see SCENARIO_TRUSTED_CREATE
     */
    public string|null $account;

    /**
     * Текст уведомления
     */
    public string $text;

    /**
     * Разделитель получателей уведомлений
     */
    public static string $delimiter = ',';

    /**
     * Типы уведомлений
     */
    public array $typs = [
        'notice' => 'Уведомление',
        'warning' => 'Предупреждение'
    ];

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

    /**
     * Свойства
     */
    public function attributes(): array
    {
        return array_merge(
            parent::attributes(),
            [
                'html',
                'type'
            ]
        );
    }

    /**
     * Метки свойств
     */
    public function attributeLabels(): array
    {
        return array_merge(
            parent::attributeLabels(),
            [
                'html' => 'HTML',
                'type' => 'Тип',
                'trgt' => 'Получатели',
                'text' => 'Текст',
            ]
        );
    }

    /**
     * Правила
     */
    public function rules(): array
    {
        return array_merge(
            parent::rules(),
            [
                [
                    'html',
                    'required'
                ],
                [
                    'type',
                    'default',
                    'value' => 'notice'
                ]
            ]
        );
    }

    /**
     * Запись
     */
    public function write(): self|array|null
    {
        return $this::_write($this->text, $this->html, $this->account ?? null, $this->type);
    }

    /**
     * Запись
     *
     * @param string $html Содержимое уведомления (HTML или текст)
     * @param bool|string|null $html Содержимое уведомления (HTML или текст)
     * @param string $account Получатель уведомления (_key или "@...")
     * @param string $type Тип уведомления
     *
     * @todo Намного удобнее будет заменить _key на _id, чтобы из рёбер сразу получать аккаунт без лишних операций
     */
    public static function _write(string $text, bool|string|null $html = false, string $account = '', string $type = self::TYPE_NOTICE): self|array|null
    {
        // Инициализация
        $model = new self;
        $receiver = Account::initAccount($account)->_key ?? $account ?? throw new Exception('Не удалось инициализировать получателя');

        if ((bool) (int) $html) {
            // Получен текст в формете HTML-кода

            $model->html = $text ?? null;
        } else {
            // Получен необработанный текст

            $text = htmlspecialchars(strip_tags($text ?? null));

            $model->html = <<<HTML
                <p>$text</p>
            HTML;
        }

        if ($model->save()) {
            // Уведомление записано

            // Инициализация получателей и создание ребра
            self::searchReceiverAndConnect($model, $receiver, $type);
        }

        return null;
    }

    /**
     * Поиск получателя
     *
     * @param self $model Уведомление
     * @param string $targets Необработанный текст с получателями
     * @param string $type Тип уведомления
     */
    protected static function searchReceiverAndConnect(self $model, string $targets, string $type = self::TYPE_NOTICE): AccountEdgeNotification|array|null
    {
        // Инициализация
        $return = [];

        // Конвертация
        $accounts = array_map('trim', explode(self::$delimiter, $targets));

        foreach ($accounts as $account) {
            if (in_array('@all', $accounts, true)) {
                // Всем пользователям

                // Инициализация
                $return = [];

                foreach (Account::readAll() as $account) {
                    // Перебор всех аккаунтов

                    // Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
                    $return[] = AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type);
                }
            } else if (
                in_array('@authorized', $accounts, true)
                || in_array('@authorizeds', $accounts, true)
                || in_array('@authorised', $accounts, true)
                || in_array('@authoriseds', $accounts, true)
                || in_array('@auth', $accounts, true)
                || in_array('@autheds', $accounts, true)
            ) {
                // Всем авторизованным (админам и модераторам)

                // Инициализация
                $return = [];

                foreach (Account::readAllAuthorizeds() as $account) {
                    // Перебор всех аккаунтов

                    // Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
                    $return[] = AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type);
                }
            } else if (
                in_array('@administrator', $accounts, true)
                || in_array('@administrators', $accounts, true)
                || in_array('@admin', $accounts, true)
                || in_array('@admins', $accounts, true)
            ) {
                // Администраторам

                // Инициализация
                $return = [];

                foreach (Account::readAllAdministrators() as $account) {
                    // Перебор всех аккаунтов

                    // Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
                    $return[] = AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type);
                }
            } else if (
                in_array('@moderator', $accounts, true)
                || in_array('@moderators', $accounts, true)
                || in_array('@moder', $accounts, true)
                || in_array('@moders', $accounts, true)
            ) {
                // Модераторам

                // Инициализация
                $return = [];

                foreach (Account::readAllModerators() as $account) {
                    // Перебор всех аккаунтов

                    // Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
                    $return[] = AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type);
                }
            } else if (in_array('@test', $accounts, true)) {
                // Тестирование (отправка самому себе)

                $return[] = AccountEdgeNotification::writeSafe($model->readId(), yii::$app->user->id, $type);
            } else {
                // Найден идентификатор (подразумевается)

                if ($account = Account::searchById(Account::collectionName() . '/' . $account)) {
                    // Аккаунт найден

                    // Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
                    $return[] = AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type);
                }
            }
        }

        return $return ? $return : null;
    }

    /**
     * Конвертация типа уведомления в версию для отображения
     *
     * @param string|null $type Тип уведомления
     *
     * @return string
     */
    public function genTypeToRussian(string $type = null): string {
        return match($type ?? $this->type) {
            'notice' => 'Уведомление',
            'warning' => 'Предупреждение',
            'error' => 'Ошибка'
        };
    }
}
