<?php

declare(strict_types=1);

namespace app\models;

use app\models\traits\SearchByEdge;

/**
 * Склад
 *
 * Хранит в себе связи с инстанциями поставок, а от них и связи со всеми поставками
 */
class Warehouse extends Document
{
    use SearchByEdge;

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

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

    /**
     * Метки свойств
     */
    public function attributeLabels(): array
    {
        return array_merge(
            parent::attributeLabels(),
            [
                'name' => 'Название',
                'addr' => 'Адрес',
                'trmn' => 'Терминал',
                'actv' => 'Активность',
                'open' => 'Доступность'
            ]
        );
    }

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

    /**
     * Запись по аккаунту
     *
     * @param Account|null $account Аккаунт
     *
     * @return static|null Записанный склад
     */
    public static function writeByAccount(Account|null $account = null): ?static
    {
        // Инициализация аккаунта
        $account = Account::initAccount($account);

        // Инициализация склада
        $warehouse = new static;

        // Запись параметров склада
        $warehouse->actv = true;

        if ($warehouse->save()) {
            // Удалось записать склад в базу данных

            // Инициализация ребра: АККАУНТ -> СКЛАД
            AccountEdgeWarehouse::writeSafe($account->readId(), $warehouse->readId(), data: ['type' => 'connected']);

            return $warehouse;
        }

        return null;
    }

    /**
     * Найти по аккаунту
     *
     * @param Account|null $account Аккаунт
     * @param int $limit Ограничение по максимальному количеству
     *
     * @return mixed Склады
     *
     * @deprecated
     */
    public static function searchByAccount(Account|null $account = null, int $limit = 10): mixed
    {
        if ($account = Account::initAccount($account)) {
            // Инициализирован аккаунт

            return static::searchByEdge(
                from: 'account',
                to: 'warehouse',
                edge: 'account_edge_warehouse',
                direction: 'INBOUND',
                subquery_where: [
                    [
                        'account_edge_warehouse._from == "' . $account->readId() . '"'
                    ],
                    [
                        'account_edge_warehouse.type == "connected"'
                    ]
                ],
                where: 'account_edge_warehouse[0] != null',
                limit: $limit
            );
        }

        return null;
    }

    /**
     * Найти по поставке
     *
     * @param Supply $supply Поставка
     * @param int $limit Ограничение по максимальному количеству
     *
     * @return mixed Склады
     */
    public static function searchBySupply(Supply $supply, int $limit = 10): mixed
    {
        return static::searchByImport(Import::searchBySupply($supply, limit: 1)[0], $limit);
    }

    /**
     * Найти по инстанции поставки
     *
     * @param Import $import Инстанция поставки
     * @param int $limit Ограничение по максимальному количеству
     *
     * @return mixed Склады
     */
    public static function searchByImport(Import $import, int $limit = 10): mixed
    {
        return self::searchByEdge(
            from: 'import',
            to: 'warehouse',
            edge: 'warehouse_edge_import',
            direction: 'OUTBOUND',
            subquery_where: [
                ['warehouse_edge_import._to' => $import->readId()],
                ['warehouse_edge_import.type' => 'loaded']
            ],
            where: 'warehouse_edge_import[0] != null',
            limit: $limit
        );
    }

    /**
     * Инициализация с записью
     *
     * Читает все склады привязанные к аккаунту, либо создает новый склад
     *
     * @param Account|null $account Аккаунт
     * @param int $limit — Ограничение по максимальному количеству
     *
     * @return array Склады
     */
    public static function initWithWrite(Account|null $account = null, int $limit = 10): array
    {
        if ($account = Account::initAccount($account)) {
            // Инициализирован аккаунт

            if ($warehouses = static::searchByAccount($account, $limit)) {
                // Найдены склады

                return $warehouses;
            }

            return [static::writeByAccount()];
        }

        return [];
    }

    /**
     * Генерация списка терминалов из ДеловыеЛинии для отправителя
     *
     * Актуальное (выбранное, активное) значение записывается первым
     *
     * @param array Необработанный список терминалов
     */
    public function genListTerminalsFrom(): array
    {
        // Инициализация
        $list = [];

        $cities = Dellin::read(limit: 9999, order: ['dellin.data.name' => 'DESC']);

        foreach ($cities as $city) {
            // Перебор городов

            foreach ($city->data['terminals']['terminal'] as $termial) {
                // Перебор терминалов

                if (in_array($termial['id'], $list, true)) {
                    // Если встретился дубликат (исполняется очень часто)

                    continue;
                }

                // Запись
                $list[$termial['id']] = $city->data['name'] . ' (' . $termial['address'] . ')';
            }
        }

        return $this->syncListWithSettings($list, 'trmn');
    }

    /**
     * Синхронизация списка вариантов параметра с текущим значением из настроек
     *
     * @param array &$list Список
     * @param string $var Название параметра
     *
     * @return array Сортированный список
     */
    protected function syncListWithSettings(array &$list, string $var): array
    {
        // Инициализация текущего значения параметра в начале массива
        if (isset($this->$var)) {
            // Параметр найден в настройках аккаунта

            if (isset($list[$this->$var])) {
                // Найдено совпадение сохранённого параметра с полученным списком из поставок

                // Буфер для сохранения параметра
                $buffer = $list[$this->$var];

                // Удаление параметра
                unset($list[$this->$var]);

                // Сохранение параметра в начале массива
                $list = [$this->$var => $buffer] + $list;
            } else {
                // Совпадение не найдено

                // Сохранение параметра из данных аккаунта в начале массива
                $list = [$this->$var => $this->$var] + $list;
            }
        } else {
            // Параметр $var не найден в настройках аккаунта

            // Сохранение параметра из данных аккаунта в начале массива
            $list = ['Город отправления' => 'Город отправления'] + $list;
        }

        return $list;
    }
}
