<?php

declare(strict_types=1);

namespace app\models;

use yii;
use yii\web\UploadedFile;
use yii\imagine\Image;

use app\models\traits\SearchByEdge;
use moonland\phpexcel\Excel;

use Exception;

/**
 * Продукт (в ассортименте магазина)
 *
 * Представляет собой лот состоящий из предложений от поставщиков
 *
 * @see Supply Поставки для продуктов
 */
class Product extends Document
{
    use SearchByEdge;

    /**
     * Сценарий импорта .excel документа
     *
     * Использовать для обхода правил при загрузке файла
     */
    const SCENARIO_IMPORT_EXCEL = 'import_excel';

    /**
     * Сценарий импорта изображений
     *
     * Использовать для обхода правил при загрузке файла
     */
    const SCENARIO_IMPORT_IMAGE = 'import_image';

    /**
     * Сценарий записи товара
     */
    const SCENARIO_WRITE = 'write';

    /**
     * Файл .excel для импорта товаров
     */
    public Excel|string|array|null $file_excel = null;

    /**
     * Изображение для импорта
     */
    public UploadedFile|string|array|null $file_image = null;

    /**
     * Группа в которой состоит товар
     */
    public ProductGroup|null $group = null;

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

    /**
     * Свойства
     */
    public function attributes(): array
    {
        return array_merge(
            parent::attributes(),
            [
                'catn',
                'name',
                // В библеотеке есть баг на название DESC (неизвестно в моей или нет)
                'dscr',
                'prod',
                'dmns',
                'wght',
                'imgs',
                'time',
                'bffr',
            ]
        );
    }

    /**
     * Метки свойств
     */
    public function attributeLabels(): array
    {
        return array_merge(
            parent::attributeLabels(),
            [
                'catn' => 'Каталожный номер (catn)',
                'name' => 'Название (name)',
                'dscr' => 'Описание (dscr)',
                'prod' => 'Производитель (prod)',
                'dmns' => 'Габариты (dmns)',
                'wght' => 'Вес (wght)',
                'imgs' => 'Изображения (imgs)',
                'time' => 'Срок доставки (time)',
                'bffr' => 'Буфер',
                'file_excel' => 'Документ (file_excel)',
                'file_image' => 'Изображение (file_image)',
                'group' => 'Группа (group)'
            ]
        );
    }

    /**
     * Правила
     *
     * @todo Правило для всех трёх габаритов
     */
    public function rules(): array
    {
        return array_merge(
            parent::rules(),
            [
                [
                    'catn',
                    'required',
                    'message' => 'Заполните поля: {attribute}',
                    'on' => self::SCENARIO_WRITE,
                    'except' => [self::SCENARIO_IMPORT_EXCEL, self::SCENARIO_IMPORT_IMAGE]
                ],
                [
                    'catn',
                    'string',
                    'message' => '{attribute} должен быть строкой'
                ],
                [
                    'imgs',
                    'arrayValidator',
                    'message' => '{attribute} должен быть массивом'
                ],
                [
                    'dmns',
                    'arrayWithNumbersValidator',
                    'message' => '{attribute} должен быть массивом и хранить циферные значения'
                ],
                [
                    'file_excel',
                    'required',
                    'message' => 'Заполните поля: {attribute}',
                    'on' => self::SCENARIO_IMPORT_EXCEL
                ],
                [
                    'file_excel',
                    'file',
                    'skipOnEmpty' => false,
                    'extensions' => 'xlsx',
                    'checkExtensionByMimeType' => false,
                    'maxFiles' => 5,
                    'maxSize' => 1024 * 1024 * 30,
                    'wrongExtension' => 'Разрешены только документы в формате: ".xlsx"',
                    'message' => 'Проблема при чтении документа',
                    'on' =>  self::SCENARIO_IMPORT_EXCEL
                ],
                [
                    'file_image',
                    'required',
                    'message' => 'Загрузите изображение',
                    'on' => self::SCENARIO_IMPORT_IMAGE
                ],
                [
                    'file_image',
                    'file',
                    'skipOnEmpty' => false,
                    'extensions' => ['jpg', 'jpeg', 'png', 'gif', 'webp'],
                    'checkExtensionByMimeType' => true,
                    'maxFiles' => 10,
                    'maxSize' => 1024 * 1024 * 30,
                    'wrongExtension' => 'Разрешены только изображения в формате: ".jpg", ".jpeg", ".png", ".gif", ".webp"',
                    'message' => 'Проблема при загрузке изображения',
                    'on' =>  self::SCENARIO_IMPORT_IMAGE
                ],
                [
                    [
                        'name',
                        'prod'
                    ],
                    'string',
                    'length' => [3, 80],
                    'message' => '{attribute} должен быть строкой от 3 до 80 символов'
                ],
                [
                    'dscr',
                    'string',
                    'length' => [3, 256],
                    'message' => '{attribute} должен быть строкой от 3 до 256 символов'
                ],
                [
                    'wght',
                    'integer',
                    'min' => 0,
                    'max' => 30000,
                    'message' => '{attribute} должен иметь значение от 0 до 30000'
                ]
            ]
        );
    }

    /**
     * Запись пустого продукта
     */
    public static function writeEmpty(string $catn): ?self
    {
        // Инициализация
        $model = new self;

        // Настройки
        $model->catn = $catn;

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

    /**
     * Импорт изображений
     *
     * @return int Количество сохранённых изображений
     */
    public function importImages(): int
    {
        // Инициализация
        $amount = 0;

        if ($this->validate()) {
            // Проверка пройдена

            foreach ($this->file_image as $file) {
                // Перебор обрабатываемых изображений

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

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

                        return false;
                    };
                }


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

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

                        return false;
                    };
                }

                // Запись на сервер
                $file->saveAs(YII_PATH_PUBLIC . $catalog . '/' . $file->baseName . '.' . $file->extension . '.original');

                // Конвертация изображения для сохранения полного изображения
                Image::resize(YII_PATH_PUBLIC . $catalog . '/' . $file->baseName . '.' . $file->extension . '.original', 800, 800)
                    ->save(YII_PATH_PUBLIC . $catalog . '/' . $file->baseName . '.' . $file->extension, ['quality' => 80]);

                // Конвертация изображения для сохранения обложки (150px)
                Image::resize(YII_PATH_PUBLIC . $catalog . '/' . $file->baseName . '.' . $file->extension, 150, 150)
                    ->save(YII_PATH_PUBLIC . $catalog_h150 . '/' . $file->baseName . '.' . $file->extension, ['quality' => 80]);

                // Инициализация
                $this->imgs ?? $this->imgs = [];

                // Запись в базу данных
                $this->imgs = array_merge(
                    $this->imgs,
                    [[
                        'covr' => count($this->imgs) === 0 ? true : false,
                        'orig' => $catalog . '/' . $file->baseName . '.' . $file->extension,
                        'h150' => $catalog_h150 . '/' . $file->baseName . '.' . $file->extension
                    ]]
                );

                $this->scenario = self::SCENARIO_WRITE;

                if ($this->save()) {
                    // Изменения сохранены в базе данных

                    // Постинкрементация счётчика
                    $amount++;
                }
            }
        }

        if ($this->hasErrors()) {
            // Получены ошибки

            foreach ($this->getErrors() as $attribute    => $errors) {
                // Перебор атрибутов

                foreach ($errors as $error) {
                    // Перебор ошибок атрибутов

                    $label = $this->getAttributeLabel($attribute);

                    Notification::_write("$label: $error", type: Notification::TYPE_ERROR);
                }
            }
        }

        return $amount;
    }

    /**
     * Импорт товаров
     *
     * На данный момент обрабатывает только импорт из
     * файлов с расширением .excel
     */
    public function importExcel(): bool
    {
        // Инициализация
        $data = [];
        $amount = 0;

        if ($this->validate()) {
            foreach ($this->file_excel as $file) {
                // Перебор файлов

                // Инициализация
                $dir = YII_PATH_PUBLIC . '../assets/import/' . date('Y-m-d', time()) . '/excel/' . (yii::$app->user->identity->_key ?? 'system') . '/' . time() . '/';

                // Сохранение на диск
                if (!file_exists($dir)) {
                    mkdir($dir, 0775, true);
                }
                $file->saveAs($path = $dir . $file->baseName . '.' . $file->extension);

                $data[] = Excel::import($path, [
                    'setFirstRecordAsKeys' => true,
                    'setIndexSheetByName' => true,
                ]);
            }


            foreach ($data as $data) {
                // Перебор конвертированных файлов

                if (count($data) < 1) {
                    // Не найдены строки с товарами

                    $this->addError('erros', 'Не удалось найти данные товаров');
                } else {
                    // Перебор найденных товаров

                    foreach ($data as $doc) {
                        // Перебор полученных документов

                        // Сохранение в базе данных
                        $product = new static($doc);

                        $product->scenario = $product::SCENARIO_WRITE;

                        if ($product->validate()) {
                            // Проверка пройдена

                            // Запись документа
                            $product->save();

                            // Постинкрементация счётчика
                            $amount++;

                            // Запись группы
                            // $group = static::class . 'Group';
                            // (new $group())->writeMember($product, $this->group);
                        } else {
                            // Проверка не пройдена
                            foreach ($product->errors as $attribute => $error) {
                                $this->addError($attribute, $error);
                            }
                        }
                    }
                }
            }

            // Деинициализация
            $this->file_excel = '';

            static::afterImportExcel($amount);

            return true;
        }

        $this->addError('erros', 'Неизвестная ошибка');

        static::afterImportExcel($amount);

        return false;
    }

    /**
     * Поиск по каталожному номеру
     *
     * Ищет продукт и возвращает его,
     * либо выполняет поиск через представление
     *
     * @todo Переделать нормально
     */
    public static function searchByCatn(string $catn, int $limit = 1, array $select = []): static|array|null
    {
        if ($limit <= 1) {
            return static::findOne(['catn' => $catn]);
        }

        $query = self::find()
            ->where(['catn' => $catn])
            ->limit($limit)
            ->select($select)
            ->createCommand()
            ->execute()
            ->getAll();

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

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

        return $query;
    }

    /**
     * Поиск по каталожному номеру (через представления)
     *
     * Ищет продукт и возвращает его,
     * либо выполняет поиск через представление
     *
     * @todo Переделать нормально
     */
    public static function searchByPartialCatn(string $catn, int $limit = 1, array $select = []): static|array|null
    {
        $query = self::find()
            ->for('product')
            ->in('product_search')
            ->filter(['catn' => $catn], 'START_SENSETIVE')
            ->limit($limit)
            ->orderBy(['catn' => 'ASC'])
            ->select($select)
            ->createCommand()
            ->execute()
            ->getAll();

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

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

        return $query;
    }

    /**
     * Вызывается после загрузки поставок из excel-документа
     *
     * @param int $amount Количество
     */
    public static function afterImportExcel(int $amount = 0): bool
    {
        // Инициализация
        $model = new Notification;
        $date = date('H:i d.m.Y', time());

        // Настройка
        $model->text = yii::$app->controller->renderPartial('@app/views/notification/system/afterImportExcel', compact('amount', 'date'));
        $model->type = $model::TYPE_NOTICE;

        // Отправка
        return (bool) $model->write();
    }

    /**
     * Вызывается после загрузки поставок из 1С
     */
    public static function afterImport1c(): bool
    {
        // Инициализация
        $model = new Notification;
        $date = date('H:i d.m.Y', time());

        // Настройка
        $model->text = yii::$app->controller->renderPartial('@app/views/notification/system/afterImport1c', compact('date'));
        $model->type = $model::TYPE_NOTICE;

        // Отправка
        return (bool) $model->write();
    }

    /**
     * Найти по идентификатору поставки
     *
     * @param string|null $_id Идентификатор поставки
     *
     * @return self|null Привязанный продукт
     */
    public static function searchBySupplyId(string $_id): ?array
    {
        return static::searchByEdge(
            from: 'supply',
            to: 'product',
            edge: 'supply_edge_product',
            direction: 'INBOUND',
            subquery_where: [
                [
                    'supply_edge_product._from == "' . $_id . '"'
                ],
                [
                    'supply_edge_product._to == product._id'
                ]
            ],
            subquery_select: 'product',
            where: 'supply_edge_product[0]._id != null',
            limit: 1,
            select: 'supply_edge_product[0]'
        )[0];
    }
}
