<?php

declare(strict_types=1);

namespace app\models;

use yii;

use mirzaev\yii2\arangodb\ActiveRecord;

use Exception;

/**
 * Документ
 */
abstract class Document extends ActiveRecord
{
    /**
     * Имя коллекции
     */
    public static function collectionName(): string
    {
        throw new Exception('Не инициализировано название коллекции');

        return 'document';
    }

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

    /**
     * Метки свойств
     */
    public function attributeLabels(): array
    {
        return [
            '_key' => 'Ключ',
            'jrnl' => 'Журнал'
        ];
    }


    /**
     * Правила
     *
     * @todo Добавить проверку существования аккаунта
     */
    public function rules(): array
    {
        return [];
    }

    /**
     * Перед сохранением
     *
     * @todo Подождать обновление от ебаного Yii2 и добавить
     * проверку типов передаваемых параметров
     */
    public function beforeSave($create): bool
    {
        if (parent::beforeSave($create)) {
            // Пройдена родительская проверка

            if ($this->isNewRecord) {
                // Новая запись

                // Запись в журнал
                $this->jrnl = array_merge(
                    [[
                        'date' => time(),
                        'account' => yii::$app->user->id ?? 'system',
                        'action' => 'create'
                    ]],
                    $this->jrnl ?? []
                );
            }

            return true;
        }

        return false;
    }

    /**
     * Журнал
     *
     * Записывает данные в журнал
     *
     * @param string $action
     *
     * @return int|bool Время записанное в журнале или false, если не удалось записать
     */
    public function journal(string $action = 'update', array ...$data): int|bool
    {
        // Инициализация
        if (isset($this->jrnl) && is_array($this->jrnl)) {
        } else {
            $this->jrnl = [];
        }

        // Генерация
        $this->jrnl = array_merge(
            $this->jrnl,
            [array_merge(
                [
                    'date' => $time = time(),
                    'account' => yii::$app->user->id ?? 'system',
                    'action' => $action
                ],
                [
                    'data' => $data
                ]
            )]
        );

        // Запись и возврат
        return $this->update() ? $time : false;
    }

    /**
     * Чтение идентификатора
     */
    public function readId(): ?string
    {
        return isset($this->_key) && static::collectionName() ? static::collectionName() . '/' . $this->_key : null;
    }

    /**
     * Поиск
     */
    public static function search(array $where, $limit = 1): array
    {
        return static::find()->where($where)->limit($limit)->all();
    }


    /**
     * Поиск по идентификатору
     */
    public static function searchById(string $_id): ?static
    {
        return static::findOne(['_id' => $_id]);
    }

    public static function readLast(): static|null|bool
    {
        return static::find()->orderBy(['DESC'])->one();
    }

    /**
     * Чтение всех записей
     *
     * @deprecated
     */
    public static function readAll(): array
    {
        return static::find()->all();
    }

    /**
     * Чтение записей по максимальному ограничению
     */
    public static function read(?array $where = [], int $limit = 100, int $page = 1, ?array $order = null): array
    {
        return static::find()->where($where)->orderby($order)->limit($limit)->offset(($page - 1) * $limit)->all();
    }

    /**
     * Чтение количества записей
     */
    public static function readAmount(): int
    {
        return static::find()->count();
    }

    /**
     * Проверка на то, что в свойство передан массив
     */
    public function arrayValidator(string $attribute, array $params = null): void
    {
        if (is_array($this->$attribute)) {
            return;
        } else {
            $this->addError($attribute, 'Передан не массив');
        }

        $this->addError($attribute, 'Не пройдена проверка: "arrayValidator"');
    }

    /**
     * Проверка на то, что в свойство передан массив и он хранит циферные значения
     */
    public function arrayWithNumbersValidator(string $attribute, array $params = null): void
    {
        try {
            if (is_array($this->$attribute)) {

                foreach ($this->$attribute as $value) {
                    if (!(bool) preg_match('/^[0-9\.]*$/m', $value)) {
                        $this->addError($attribute, 'В массиве найдены запрещённые символы');
                    }
                }

                return;
            } else {
                $this->addError($attribute, 'Передан не массив');
            }
        } catch (Exception $e) {
            $this->addError($attribute, $e->getMessage());
        }

        $this->addError($attribute, 'Не пройдена проверка: "arrayWithNumbersValidator"');
    }

    /**
     * Конвертировать _id в _key
     */
    private static function keyFromId(string $_id): ?string
    {
        preg_match_all('/\/([0-9]+)$/m', $_id, $mathes);

        return $mathes[1][0] ?? null;
    }

    /**
     * Статический вызов
     *
     * @param string $name
     * @param array $args
     */
    public static function __callStatic(string $name, array $args): mixed {
        return match ($name) {
            'keyFromId' => self::keyFromId(...$args)
        };
    }
}
