<?php

declare(strict_types=1);

namespace app\models;

use yii;
use yii\web\User as Account;

use app\models\traits\SearchByEdge;

use Exception;

/**
 * Заказ
 *
 * @see Account Заказчик
 * @see Supply Поставки для заказа
 */
class Order extends Document
{
    use SearchByEdge;

    /**
     * Поставки для записи
     */
    public array $supplies;

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

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

    /**
     * Метки свойств
     */
    public function attributeLabels(): array
    {
        return array_merge(
            parent::attributeLabels(),
            []
        );
    }

    /**
     * Правила
     */
    public function rules(): array
    {
        return array_merge(
            parent::rules(),
            []
        );
    }

    /**
     * Запись
     *
     * $supply = [ Supply $supply, int $amount = 1 ]
     *
     * @param Supply|array $supply Поставка
     * @param Account $trgt Заказчик
     *
     * @todo Создать параметр разделителя для администрации
     */
    public function write(Supply|array $supply, Account $trgt = null): self|null
    {
        // Инициализация
        $trgt ?? $trgt = yii::$app->user ?? throw new Exception('Не удалось инициализировать заказчика');

        if ($supply instanceof Supply) {
            // Передана инстанция класса поставки

            // Унификация входных данных
            $supply = [$supply, 1];
        }

        if (is_null($this->_key)) {
            // Корзина не инициализирована

            // Инициализация
            if (!$this->save()) {
                // Инициализация заказа не удалась

                throw new Exception('Ошибка при записи заказа в базу данных');
            }

            // Инициализация ребра: АККАУНТ -> ЗАКАЗ
            if (!AccountEdgeOrder::write($trgt->readId(), $this->readId(), 'create')) {
                // Инициализация не удалась

                throw new Exception('Ошибка при записи ребра от аккаунта до заказа в базу данных');
            }
        }

        foreach ($supply as $supply_raw) {
            // Перебор поставок

            for ($i = 0; $i < $supply_raw[1]; $i++) {
                // Создание рёбер соразмерно запросу (добавление нескольких продуктов в корзину)

                // Инициализация ребра: ЗАКАЗ -> ПОСТАВКА
                if (!$supply = Supply::searchByCatn($supply_raw[0]) or !OrderEdgeSupply::write($this->readId(), $supply->readId(), 'add')) {
                    // Поставка не найдена или инициализация ребра не удалась

                    continue;
                }
            }
        }

        // Отправка на сервер базы данных
        return $this->save() ? $this : null;
    }

    /**
     * Поиск заказа
     */
    public static function search(Account $account = null, string $type = 'current', int $limit = 1): self|array
    {
        $account or $account = yii::$app->user ?? throw new Exception('Не удалось инициализировать пользователя');

        $return = self::searchByEdge(
            from: 'account',
            to: 'order',
            subquery_where: [
                [
                    'account._id' => $account->id
                ],
                [
                    'account_edge_order.type' => $type
                ]
            ],
            limit: $limit,
            sort: ['DESC'],
            direction: 'INBOUND'
        );

        return $limit === 1 ? $return[0] : $return;
    }

    /**
     * Поиск содержимого заказа
     *
     * @todo В будущем возможно заказ не только поставок реализовать
     */
    public function content(int $limit = 1, int $page = 1): Supply|array
    {
        // Генерация сдвига по запрашиваемым данным (пагинация)
        $offset = $limit * ($page - 1);

        // Поиск рёбер: ЗАКАЗ -> ПОСТАВКА
        $return = Supply::searchByEdge(
            from: 'order',
            to: 'supply',
            edge: 'order_edge_supply',
            subquery_where: [
                [
                    'order._id' => $this->readId()
                ],
                [
                    'order_edge_supply.type' => 'add'
                ]
            ],
            foreach: ['edge' => 'order_edge_supply'],
            where: 'edge._to == supply._id',
            limit: $limit,
            offset: $offset
        );

        return $limit === 1 ? $return[0] : $return;
    }
}
