<?php

declare(strict_types=1);

namespace app\models;

/**
 * Ребро
 */
abstract class Edge extends Document
{
    /**
     * Свойства
     */
    public function attributes(): array
    {
        return array_merge(
            parent::attributes(),
            [
                '_from',
                '_to',
                'type'
            ]
        );
    }

    /**
     * Метки свойств
     */
    public function attributeLabels(): array
    {
        return array_merge(
            parent::attributeLabels(),
            [
                '_from' => 'От кого',
                '_to' => 'К кому',
            ]
        );
    }

    /**
     * Правила
     */
    public function rules(): array
    {
        return array_merge(
            parent::rules(),
            [
                [
                    ['_from', '_to'],
                    'required',
                    'message' => 'Заполните поле: {attribute}'
                ],
                [
                    'type',
                    'string'
                ]
            ]
        );
    }

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

            return true;
        }

        return false;
    }

    /**
     * Записать (с проверкой на существование)
     *
     * Создаст ребро только в том случае, если его аналога не существует
     */
    public static function writeSafe(string $_from, string $_to, string $type = '', array $data = []): ?static
    {
        if ($edge = self::searchByVertex($_from, $_to, limit: 1)) {
            // Найдено в базе данных

            return $edge;
        }

        return self::write($_from, $_to, $type, $data);
    }

    /**
     * Записать
     */
    public static function write(string $_from, string $_to, string $type, array $data = []): ?static
    {
        // Инициализация
        $edge = new static;

        // Настройка
        $edge->_from = $_from;
        $edge->_to = $_to;
        $edge->type = $type;

        foreach ($data as $key => $value) {
            if (is_int($key)) {
                // Обычная запись

                $edge->{$value} = true;
            } else {
                // Ассоциативная запись

                $edge->{$key} = $value;
            }
        }

        return $edge->save() ? $edge : null;
    }

    /**
     * Поиск ребра по его вершинам
     */
    public static function searchByVertex(string $_from, string $_to, string|null $type = null, int $limit = 1, array|null $filter = null): array|null
    {
        $query = self::find()->where([
            '_from' => $_from,
            '_to' => $_to
        ]);

        if (isset($type)) {
            $query->where(['type' => $type]);
        }

        if (isset($filter)) {
            $query->where($filter);
        }

        return $query->limit($limit)->all();
    }

    /**
     * Поиск рёбер
     */
    public static function search(string $target, string $direction = 'OUTBOUND', string $type = '', int $limit = 1): static|array|null
    {
        if ($direction === 'OUTBOUND') {
            $query = self::find()->where(['_from' => $target, 'type' => $type]);
        } else if ($direction === 'INBOUND') {
            $query = self::find()->where(['_to' => $target, 'type' => $type]);
        }

        if ($limit < 2) {
            return $query->one();
        } else {
            return $query->limit($limit)->all();
        }
    }
}
