<?php

declare(strict_types=1);

namespace app\models;

use app\models\traits\SearchByEdge;

use carono\exchange1c\interfaces\GroupInterface;

use Zenwalker\CommerceML\Model\Group;

/**
 * Группировка продуктов для соединения их в аналоги
 */
class ProductGroup extends Document implements GroupInterface
{
    use SearchByEdge;

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

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

    /**
     * Метки свойств
     */
    public function attributeLabels(): array
    {
        return array_merge(
            parent::attributeLabels(),
            [
                'stts' => 'Статус'
            ]
        );
    }

    /**
     * Правила
     */
    public function rules(): array
    {
        return array_merge(
            parent::rules(),
            [
                [
                    'stts',
                    'string',
                    'length' => [4, 20],
                    'message' => '{attribute} должен быть строкой от 4 до 20 символов'
                ]
            ]
        );
    }

    /**
     * Запись пустой группы
     *
     * @param bool $active Статус активации
     *
     * @return self Группа товаров, если создана
     */
    public static function writeEmpty(bool $active = false): ?self
    {
        // Инициализация
        $model = new self;

        // Настройки
        $model->stts = $active ? 'active' : 'inactive';

        // Запись
        return $model->save() ? $model : null;
    }

    /**
     * Запись члена группы
     *
     * @deprecated
     */
    public function writeMember(Product $member): ProductEdgeProductGroup
    {
        return ProductEdgeProductGroup::write($member->readId(), $this->readId(), 'member');
    }

    /**
     * Запись товара в группу
     *
     * @param Product $product Товар
     */
    public function writeProduct(Product $product): ?ProductEdgeProductGroup
    {
        // Запись товара в группу
        $edge = ProductEdgeProductGroup::write($product->readId(), $this->readId(), data: ['type' => 'member']);

        // Запись в журнал
        $this->journal('write member', [
            'product' => $product->readId()
        ]);

        return $edge;
    }
    /**
     * Удаление товара из группы
     *
     * @param Product $product Товар
     *
     * @return void
     */
    public function deleteProduct(Product $product): void
    {
        // Удаление товара из группы (подразумевается, что будет только одно)
        foreach (ProductEdgeProductGroup::searchByVertex($product->readId(), $this->readId(), filter: ['type' => 'member']) as $edge) $edge->delete();

        // Запись в журнал
        $this->journal('delete member', [
            'product' => $product->readId()
        ]);
    }

    /**
     * Найти рёбра до товаров
     *
     * @param int $limit Ограничение по максимальному количеству
     */
    public function searchEdges(int $limit = 100, int $page = 1): ?array
    {
        return ProductEdgeProductGroup::searchByDirection($this->readId(), 'INBOUND', where: ['type' => 'member'], limit: $limit, page: $page);
    }

    /**
     * Прочитать связанные товары
     *
     * @param int $limit Ограничение по максимальному количеству
     */
    public function searchProducts(int $limit = 100, int $page = 1): ?array
    {
        // Инициализация буфера товаров
        $products = [];

        foreach ($this->searchEdges($limit, $page) as $edge) {
            // Перебор рёбер

            $products[] = Product::searchById($edge->_from);
        }

        return $products;
    }

    /**
     * Перенос членов группы из другой
     *
     * @param ProductGroup $group Группа из которой нужен перенос
     *
     * @return null|int Количество перенесённых товаров, если произведён перенос
     */
    public function transfer(ProductGroup $group): ?int
    {
        // Проверка на то, что запрошен перенос "из себя в себя"
        if ($this->readId() === $group->readId()) return null;

        // Инициализация счётчика записанных товаров
        $transfered = 0;

        // Перенос
        foreach ($group->searchProducts() as $product) if ($this->writeProduct($product)) ++$transfered;

        // Деактивация целевой группы (пустой)
        $group->deactivate();

        // Запись в журнал
        $this->journal('transfer', [
            'from' => $group->readId()
        ]);

        return $transfered;
    }

    /**
     * Деактивация
     *
     * @return bool Статус выполнения
     */
    public function deactivate(): bool
    {
        $this->stts = 'inactive';

        if ($this->update() > 0) return true;

        return false;
    }

    /**
     * Запись рёбер групп
     *
     * Создание взаимоотношений между группами по типу древовидной системы
     */
    public static function createTree1c($groups): Document|null
    {
        foreach ($groups as $group) {
            // Перебор групп

            // Создание
            $parent = static::createByML($group);

            if ($children = $group->getChildren()) {
                // Найден потомок

                // Вход в рекурсию
                $children = static::createTree1c($children);

                // Запись рёбер с родителем
                static::writeEdgeBetweenGroup($parent::collectionName() . '/' . $parent->_key, $children::collectionName() . '/' . $children->_key);

                return $children;
            }

            return $parent;
        }
    }

    /**
     * Запись группы
     *
     * Создаём группу по модели группы CommerceML
     * проверяем все дерево родителей группы,
     * если родителя нет в базе - создаём
     */
    public static function createByML(Group $group): static|array|null
    {
        // if (!$model = static::readByOnecId($group->id)) {
        //     // Группа не найдена

        //     // Инициализация
        //     $model = new static;

        //     $model->onec_id = $group->id;
        // }

        // $model->name = $group->name;

        // if ($parent = $group->getParent()) {
        //     // Найден родитель

        //     // Инициализация (рекурсия)
        //     $parentModel = static::createByML($parent);

        //     $model->onec_prnt_id = $parentModel->id;

        //     unset($parentModel);
        // } else {
        //     $model->onec_prnt_id = null;
        // }

        // $model->save();

        // return $model;

        return null;
    }

    private static function writeEdgeBetweenGroup(string $from, string $to): bool
    {
        // Инициализация
        $edge = new ProductGroupEdgeProductGroup();

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

        // Запись
        return $edge->save();
    }

    public static function readByName(string $name)
    {
        return static::findOne(['name' => $name]);
    }

    /**
     * Название поля в котором хранится ID из 1C
     */
    public static function getIdFieldName1c(): string
    {
        return 'onec_id';
    }

    public static function readByOnecId(string $onec_id): ?ProductGroup
    {
        return static::findOne(['onec_id' => $onec_id]);
    }

    /**
     * Найти по идентификатору товара
     *
     * @param Product $product Товар
     *
     * @return self|null Группа (ProductGroup)
     */
    public static function searchByProduct(Product $product): ?self
    {
        return static::searchByEdge(
            from: 'product',
            to: 'product_group',
            edge: 'product_edge_product_group',
            direction: 'INBOUND',
            subquery_where: [
                [
                    'product_edge_product_group._from == "' . $product->readId() . '"'
                ]
            ],
            subquery_select: 'product_group',
            where: 'product_edge_product_group[0]._id != null',
            limit: 1
        )[0] ?? null;
    }
}
