<?php

declare(strict_types=1);

namespace app\models;

use Yii;

use app\models\Product;
use app\models\SupplyEdgeProduct;
use app\models\traits\Xml2Array;

use carono\exchange1c\interfaces\ProductInterface;

use exception;

/**
 * Поставка (выгрузка товаров от поставщиков)
 *
 * Представляет собой предложения от поставщиков которые добавляются
 * в универсальные лоты товаров в асспортименте магазина
 *
 * @see Product Продукт (туда добавляются поставки)
 */
class Supply extends Product implements ProductInterface
{
    /**
     * Метод для конвертации XML в Array
     */
    use Xml2Array;

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

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

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

    /**
     * После сохранения
     */
    public function afterSave($data, $vars): void
    {
        if (AccountEdgeSupply::searchByVertex(Yii::$app->user->id, $this->readId())) {
            // Ребро уже существует

        } else {
            // Ребра не существует

            // Запись ребра: АККАУНТ -> ПОСТАВКА
            (new AccountEdgeSupply)->write(Yii::$app->user->id, $this->readId(), 'import');
        }
    }

    /**
     * Запись реквизитов из 1С
     */
    public function setRequisite1c($name, $value): mixed
    {
        return true;
    }

    /**
     * Запись группы из 1С
     */
    public function setGroup1c($group): mixed
    {
        // Чтение группы
        // if ($group = SupplyGroup::readByOnecId($group->id)) {
        //     // Запись ребра: ПОСТАВКА => ГРУППА ПОСТАВОК
        //     return static::writeEdgeBetweenGroup(static::collectionName() . '/' . $this->_key, $group->collectionName() . '/' . $group->_key);
        // }

        return true;
    }

    /**
     * Запись данных свойств по UUID 1C
     *
     * Ищет записанные свойства из 1C по их идентификатору и добавляет к ним
     * недостающие данные. Это костыль оставшийся от реляционных баз данных
     *
     * @todo Понять что может храниться внутри "$model->onec['ЗначенияСвойств']['ЗначенияСвойства']" и переписать
     */
    public static function createProperties1c($properties): void
    {
        // Инициализация
        $models = self::searchByAccount(Yii::$app->user->id, true);
        $properties = self::xml2array($properties->xml);

        // for ($i = 0; $i <= count($models); $i++)
        foreach ($models as $model) {
            // Перебор записей

            // Инициализация
            $changes = false;
            $transit = $model->onec;

            foreach ($model->onec['ЗначенияСвойств'] as $attribute_name => $attribute_value) {
                // Перебор аттрибутовfw

                foreach ($properties as $property) {
                    // Перебор свойств

                    if (is_array($attribute_value) && is_array($property) && $attribute_value['Ид'] === $property['Ид']) {
                        // Совпадение идентификаторов

                        // Объединение данных
                        $transit['ЗначенияСвойств'][$attribute_name] = array_merge($attribute_value, $property, $transit['ЗначенияСвойств'][$attribute_name]);

                        // Запись индикатора наличия изменений
                        $changes = true;

                        // break;
                    } else {
                        // Объединение данных
                        $transit['ЗначенияСвойств'][$attribute_name] = $property;
                    }
                }
            }

            if ($changes) {
                // Если указано, что записаны изменения

                // Настройка ($transit нужен из-за ограничений __set())
                $model->onec = $transit;

                foreach ($model->onec['ЗначенияСвойств'] as $property) {
                    // Перебор всех свойств

                    if (is_array($property)) {
                        if ($property['Ид'] === 'd99622fe-4526-11eb-b7f3-f3e52d0a06a9') {
                            // Если идентификатор свойства совпадает с указанным в настройках свойства хранящего OEM номера

                            // Настройка
                            $model->catn = $property['Значение'];
                        }
                    }
                }

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

    /**
     * Запись параметров из 1С
     */
    public function setProperty1c($property): mixed
    {
        return true;
    }

    /**
     * Запись изображений из 1С
     *
     * @todo Добавить параметры в админ-панель
     * Запретить доступ к изображениям
     */
    public function addImage1c($path, $caption): bool
    {
        // Инициализация
        $i = 0;

        if (!file_exists(YII_PATH_PUBLIC . $catalog = '/img/supplies/' . $this->_key)) {
            // Директория для изображений продукта не найдена

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

                return false;
            };
        }

        foreach ($this->imgs ?? [] as $image) {
            // Перебор имеющихся изображений

            if ($path === $image['sorc']) {
                // Изображение уже записано на сервер

                return true;
            }
        }

        // Инициализация
        $urn = basename($path);

        // Запись
        copy($path, $path_local = YII_PATH_PUBLIC . $catalog . '/' . $urn);

        // Запись свойства
        $this->imgs = array_merge(
            $this->imgs ?? [],
            [
                [
                    'desc' => $caption,
                    'path' => $path_local,
                    'sorc' => $path
                ]
            ]
        );

        // Отправка в базу данных
        return $this->update();
    }

    /**
     * Запись ребра (предложения от поставок к продуктам) из 1С
     *
     * @todo Разобраться зачем нужно возвращать SupplyEdgeProduct
     */
    public function getOffer1c($offer): SupplyEdgeProduct
    {
        if (empty($this->catn)) {
            // Не передан каталожный номер

            // Разработчику библеотеки надо дать по жопе
            return new SupplyEdgeProduct;
        }

        // Инициализация п̸̨͇͑͋͠р̷̬̂́̀̊о̸̜̯̹̅͒͘͝д̴̨̨̨̟̈́̆у̴̨̭̮̠́͋̈́к̴̭͊̋̎т̵̛̣͈̔̐͆а̵̨͖͑
        $product = Product::initEmpty($this->catn);

        if (!is_array($product)) {
            // Создался только один товар и вернулся в виде модели

            $product = [$product];
        }

        if (is_array($this->oemn)) {
            // Значение OEM было инициализировано

            foreach ($this->oemn as $oem) {
                // Перебор артикулов из массива ОЕМ-номеров

                // Инициализация и запись
                $product[] = Product::initEmpty($oem);
            }
        }



        // $fp = fopen('555.txt', 'a');
        // fwrite($fp, print_r($property, true) . PHP_EOL);
        // fclose($fp);


        foreach ($product as $product) {
            // Перебор всех инициализированных продуктов

            if (SupplyEdgeProduct::searchByVertex($this->readId(), $product->readId())) {
                // Ребро уже существует

                continue;
            }

            // Запись ребра: ПОСТАВКА -> ПРОДУКТ
            $return = (new SupplyEdgeProduct)->write(
                $this->readId(),
                $product->readId(),
                'sell',
                [
                    'onec' => self::xml2array($offer->xml)
                ]
            );
        }

        // Возвращает последнее сохранённое ребро
        // Надо будет с этим разобраться
        return $return ?? new SupplyEdgeProduct();
    }

    /**
     * Запись продукта из 1С
     *
     * @todo Понять что может храниться внутри "$model->onec['ЗначенияСвойств']['ЗначенияСвойства']" и переписать
     */
    public static function createModel1c($product): ?self
    {
        // Инициализация
        $model = self::searchByOnecId($id = (string) $product->Ид) ?? new self;

        // Настройка
        $model->ocid = $id ?? null;
        $model->catn = (string) $product->Артикул;
        $model->onec = self::xml2array($product->xml);

        if (isset($model->onec['ЗначенияСвойств'])) {
            // Свойства инициализированы

            foreach ($model->onec['ЗначенияСвойств'] as $property) {
                // Перебор всех свойств

                if (is_array($property)) {
                    if ($property['Ид'] === 'd99622fe-4526-11eb-b7f3-f3e52d0a06a9') {
                        // Если идентификатор свойства совпадает с указанным в настройках свойства хранящего OEM номера

                        // Настройка
                        $model->oemn = array_merge(self::convertOemn2Catn($property['Значение']), self::convertOemn2Catn((string) $product->Артикул));
                    }
                }
            }
        }

        // Запись
        if ($model->save()) {
            // Поставка успешно сохранена

            return $model;
        }

        return null;
    }

    /**
     * Запись цены из 1С
     */
    public function setPrice1c($price): mixed
    {
        return true;
    }

    /**
     * Запись данных на случай ошибки при экспорте из 1С
     */
    public function setRaw1cData($cml, $object): bool
    {
        return true;
    }

    /**
     * Поиск по идентификатору из 1С
     *
     * @param string $ocid Идентификатор из 1С
     *
     * @return Supply|null
     */
    public static function searchByOnecId(string $ocid): ?Supply
    {
        return static::findOne([static::getIdFieldName1c() => $ocid]);
    }

    /**
     * Чтение группы из 1С
     */
    public function getGroup1c(): ?SupplyGroup
    {
        return $this->group;
    }

    /**
     * Чтение названия поля в котором хранится идентификатор из 1С
     */
    public static function getIdFieldName1c(): string
    {
        return 'ocid';
    }

    /**
     * Поиск через связь с аккаунтом
     *
     * @param string $id Идентификатор пользователя
     * @param bool $full Возврат всех значений (иначе только свойства)
     */
    public static function searchByAccount(string $id = null, bool $full = false): array
    {
        if (!isset($id)) {
            $id = yii::$app->user->id ?? throw new exception('Не найден идентификатор');
        }

        $subquery = static::find()
            ->for(['account', 'account_edge_supply'])
            ->traversal('supply')->in('account_edge_supply')
            ->where(['account._id' => $id])
            ->select('account_edge_supply')
            ->createCommand();

        $query = static::find()
            ->params($subquery->getBindVars())
            ->let('account_edge_supply', '(' . (string) $subquery . ')')
            ->where('supply._id == account_edge_supply[0]._to')
            ->andWhere('supply.onec["ЗначенияСвойств"] != null');

        if ($full) {
            // Если указан полный поиск
            return $query->select('supply')->all();
        }

        $query = $query->select('supply.onec["ЗначенияСвойств"]')->createCommand()->execute()->getAll();

        foreach ($query as &$attribute) {
            // Приведение всех свойств в массив и очистка от лишних данных

            $attribute = $attribute->getAll();
        }

        return $query;
    }
}
