<?php

declare(strict_types=1);

namespace app\controllers;

use yii;
use yii\web\Controller;
use yii\web\Response;

use app\models\Account;
use app\models\Product;
use app\models\Supply;
use app\models\Search;

use app\models\connection\Dellin;
use app\models\Settings;
use Exception;

class SearchController extends Controller
{
    /**
     * @todo Сессию привязать к аккаунту и проверку по нему делать, иначе её можно просто сбрасывать
     */
    public function actionIndex(): array|string
    {
        // Инициализация параметров
        $auth_only = false;

        if ($auth_only && yii::$app->user->isGuest) {
            // Если активирован режим "Поиск только аутентифицированным" и запрос пришел не от аутентифицированного

            // Запись кода ответа: 401 (необходима авторизация)
            yii::$app->response->statusCode = 401;

            // Переход к концу обработки
            goto skip_search;
        }

        // Инициализация
        $query = yii::$app->request->post('request') ?? yii::$app->request->get('q');

        if (yii::$app->request->post('type') === 'product' || yii::$app->request->get('type') === 'product') {
            // Поиск по продуктам

            if (yii::$app->request->post('history')) {
                // Запрошена история

                yii::$app->response->format = Response::FORMAT_JSON;

                return [
                    'panel' => $this->renderPartial('/search/panel', ['history' => true]),
                    '_csrf' => yii::$app->request->getCsrfToken()
                ];
            }

            // Инициализация сессии
            $session = yii::$app->session;

            // Инициализация ответа
            $response = null;

            // Инициализация параметров
            $timer = 0;

            // Период пропуска запросов (в секундах)
            $period = 3;
            $keep_connect = false;
            $sanction = false;
            $sanction_condition = ($session['last_request'] + $period - time()) < $period;
            $sanction_time = 2;
            $query_min = 2;
            $query_max = 20;

            if (isset($session['last_request'])) {
                // Данные о времени последнего запроса не найдены

                if ($sanction && $sanction_condition) {
                    // Наказание за повторный запрос при условии задержки

                    $session['last_request'] += $sanction_time;
                }
            } else {
                // Это первый запрос

                // Инициализация
                $session['last_request'] = time();

                // Пропуск проверок
                goto first_request;
            }

            // Метка: "Повтор обработки поиска" (для создания непрерывного соединения)
            keep_connect_wait:

            // Запись времени последнего запроса и вычисление об истечении таймера
            $timer = $session['last_request'] + $period - time();

            if ($timer > 0) {
                // Время блокировки не истекло

                // Запись кода ответа: 202 (запрос не обработан)
                yii::$app->response->statusCode = 202;

                $return = [
                    'timer' => $timer,
                    'panel' => $this->renderPartial('/search/loading'),
                    '_csrf' => yii::$app->request->getCsrfToken()
                ];
            } else {
                // Запрос

                // Очистка времени последнего запроса
                unset($session['last_request']);

                // Метка: "Первый запрос"
                first_request:

                if (strlen($query) < $query_min) {
                    // Выход за ограничения длины с недостатком

                    // Пропуск поиска
                    goto skip_query;
                } else if (strlen($query) > $query_max) {
                    // Выход за ограничения длины с превышением

                    // Пропуск поиска
                    goto skip_query;
                }

                // Запись в историю
                Search::write($query);

                $limit = yii::$app->request->isPost ? 10 : 20;

                if ($response = Product::searchByPartialCatn($query, $limit, [
                    '_key' => '_key',
                    'catn' => 'catn',
                    'name' => 'name',
                    // Баг с названием DESC
                    'dscr' => 'dscr',
                    'catg' => 'catg',
                    'imgs' => 'imgs',
                    'prod' => 'prod',
                    'dmns' => 'dmns',
                ])) {
                    // Данные найдены по поиску в полях Каталожного номера

                    foreach ($response as &$row) {
                        // Перебор продуктов

                        // Поиск поставок привязанных к продуктам
                        $connections = Supply::searchByEdge(
                            from: 'product',
                            to: 'supply',
                            edge: 'supply_edge_product',
                            limit: 11,
                            direction: 'OUTBOUND',
                            subquery_where: [
                                ['product._key' => $row['_key']],
                                ['supply.catn == product.catn'],
                                ['supply_edge_product.type' => 'connect']
                            ],
                            where: 'supply._id == supply_edge_product[0]._from',
                            select: '{supply, supply_edge_product}'
                        );

                        // Инициализация буфера
                        $buffer_connections = [];

                        if (count($connections) === 11) {
                            // Если в базе данных хранится много поставок

                            // Инициализация
                            $row['overload'] = true;
                        }

                        foreach ($connections as $key => &$connection) {
                            // Перебор поставок

                            if (($cost = $cost['ЦенаЗаЕдиницу'] ?? $connection['supply_edge_product'][0]['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) < 1
                                || ($amount = $connection['supply']['amnt'] ?? $connection['supply_edge_product'][0]['onec']['Количество']) < 1
                            ) {
                                // Цена меньше единицы (подразумевается как ошибка) или количество меньше единицы

                                // Скрыть из выдачи
                                unset($connections[$key]);

                                continue;
                            }

                            // Инициализация аккаунта
                            $connection['account'] = Account::searchBySupplyId($connection['supply_edge_product'][0]['_from']);

                            // Инициализация продукта
                            $connection['product'] = Product::searchBySupplyId($connection['supply_edge_product'][0]['_from']);

                            try {
                                // Инициализация данных геолокации

                                try {
                                    $from = (int) $connection['account']['opts']['delivery_from_terminal'] ?? empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default;
                                } catch (Exception $e) {
                                    $from = empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default;
                                }

                                try {
                                    $to = (int) yii::$app->user->identity->opts['delivery_to_terminal'] ?? 36;
                                } catch (Exception $e) {
                                    $to = 36;
                                }

                                if (
                                    ($buffer_connection = $connection['product']['bffr']["$from-$to"] ?? false)
                                    && time() < $buffer_connection['expires']
                                ) {
                                    // Найдены данные доставки в буфере
                                    // и срок хранения не превышен, информация актуальна

                                    // Запись в буфер вывода
                                    $connection['delivery'] = $buffer_connection['data'];
                                    $connection['delivery']['type'] = 'auto';
                                } else {
                                    // Инициализация инстанции продукта в базе данных
                                    $product = Product::searchByCatn($connection['product']['catn']);

                                    // Инициализация доставки Dellin (автоматическая)
                                    $product->bffr = [
                                        "$from-$to" => [
                                            'data' => $connection['delivery'] = Dellin::calcDeliveryAdvanced(
                                                $from,
                                                $to,
                                                (int) ($connection['product']['wght'] ?? 0),
                                                (int) ($connection['product']['dmns']['x'] ?? 0),
                                                (int) ($connection['product']['dmns']['y'] ?? 0),
                                                (int) ($connection['product']['dmns']['z'] ?? 0)
                                            ),
                                            'expires' => time() + 86400
                                        ]
                                    ] + ($product->bffr ?? []);
                                    $connection['delivery']['type'] = 'auto';

                                    // Отправка в базу данных
                                    $product->update();
                                }
                            } catch (Exception $e) {
                                $connection['delivery']['error'] = true;

                                // var_dump($e->getMessage());
                                // var_dump($e->getTrace());
                                // var_dump($e->getFile());

                                // var_dump(json_decode($e->getMessage(), true)['errors']);
                                // die;
                            }

                            // Инициализация цены (цена поставки + цена доставки + наша наценка)
                            $connection['cost'] = $cost + ($connection['delivery']['price']['all'] ?? $connection['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0);

                            // Инициализация версии для рассчета доставки по воздуху
                            $buffer_delivery_avia = $connection;

                            try {
                                // Инициализация данных геолокации

                                if (($cost = $cost['ЦенаЗаЕдиницу'] ?? $buffer_delivery_avia['supply_edge_product'][0]['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) < 1
                                    || ($amount = $connection['supply']['amnt'] ?? $connection['supply_edge_product'][0]['onec']['Количество']) < 1
                                ) {
                                    // Цена меньше единицы (подразумевается как ошибка) или количество меньше единицы
                                    // Этот код не будет выполняться, так как цена одна на обе позиции и аналогичная проверка выше уже есть
                                    // Однако я это оставлю для возможных доработок в будущем

                                    // Скрыть из выдачи
                                    unset($connections[$key]);

                                    continue;
                                }

                                try {
                                    $from = (int) $buffer_delivery_avia['account']['opts']['delivery_from_terminal'] ?? empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default;
                                } catch (Exception $e) {
                                    $from = empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default;
                                }

                                try {
                                    $to = (int) yii::$app->user->identity->opts['delivery_to_terminal'] ?? 36;
                                } catch (Exception $e) {
                                    $to = 36;
                                }

                                if (
                                    ($buffer_connection = $buffer_delivery_avia['product']['bffr']["$from-$to-avia"] ?? false)
                                    && time() < $buffer_connection['expires']
                                ) {
                                    // Найдены данные доставки в буфере
                                    // и срок хранения не превышен, информация актуальна

                                    // Запись в буфер вывода
                                    $buffer_delivery_avia['delivery'] = $buffer_connection['data'];
                                    $buffer_delivery_avia['delivery']['type'] = 'avia';
                                } else {
                                    // Инициализация инстанции продукта в базе данных
                                    $product = Product::searchByCatn($buffer_delivery_avia['product']['catn']);

                                    // Инициализация доставки Dellin (автоматическая)
                                    $product->bffr = [
                                        "$from-$to-avia" => [
                                            'data' => $buffer_delivery_avia['delivery'] = Dellin::calcDeliveryAdvanced(
                                                $from,
                                                $to,
                                                (int) ($buffer_delivery_avia['product']['wght'] ?? 0),
                                                (int) ($buffer_delivery_avia['product']['dmns']['x'] ?? 0),
                                                (int) ($buffer_delivery_avia['product']['dmns']['y'] ?? 0),
                                                (int) ($buffer_delivery_avia['product']['dmns']['z'] ?? 0),
                                                avia: true
                                            ),
                                            'expires' => time() + 86400
                                        ]
                                    ] + ($product->bffr ?? []);
                                    $buffer_delivery_avia['delivery']['type'] = 'avia';

                                    // Отправка в базу данных
                                    $product->update();
                                }
                            } catch (Exception $e) {
                                $buffer_delivery_avia['delivery']['error'] = true;

                                // echo '<pre>';
                                // var_dump($e->getMessage());
                                // var_dump($e->getTrace());
                                // var_dump($e->getFile());

                                // var_dump(json_decode($e->getMessage(), true)['errors']);
                                // die;
                            }

                            if (!isset($buffer_delivery_avia['delivery']['error']) || $buffer_delivery_avia['delivery']['error'] !== true) {
                                // Если рассчиталась доставка самолётом

                                // Инициализация цены (цена поставки + цена доставки + наша наценка)
                                $buffer_delivery_avia['cost'] = $cost + ($buffer_delivery_avia['delivery']['price']['all'] ?? $buffer_delivery_avia['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0);

                                // Запись в буфер
                                $buffer_connections[] = $buffer_delivery_avia;
                            }
                        }

                        // Запись обработанных данных
                        $row['supplies'] = array_merge($connections, $buffer_connections);
                    }

                    // Запись ответа
                    $return = [
                        'panel' => $this->renderPartial('/search/panel', compact('response')),
                        '_csrf' => yii::$app->request->getCsrfToken()
                    ];

                    if ((int) yii::$app->request->post('advanced')) {
                        // Полноценный поиск

                        // Запись ответа
                        $return['main'] = $this->renderPartial('/search/index', compact('response'));
                        $return['hide'] = 1;
                        $return['redirect'] = '/search?type=product&q=' . $query;
                    }
                } else {
                    // Данные не найдены

                    // Запись кода ответа: 404 (запрашиваемые данные не найдены)
                    yii::$app->response->statusCode = 404;
                }
            }

            // Метка: "Пропуск запроса" (для пропуска самого поиска и возврата ответа)
            skip_query:

            if (yii::$app->request->isPost) {
                // POST-запрос

                yii::$app->response->format = Response::FORMAT_JSON;

                return $return ?? [
                    'panel' => $this->renderPartial('/search/panel'),
                    '_csrf' => yii::$app->request->getCsrfToken()
                ];
            } else {
                // GET-запрос

                if (empty($return['main']) && $keep_connect && $timer > 0) {
                    // Режим непрерывного соединения

                    // Ожидание
                    sleep($timer);

                    // Повтор обработки
                    goto keep_connect_wait;
                }

                $advanced = true;

                return $this->render('/search/index', compact('response', 'timer', 'advanced'));
            }
        }

        // Метка: "Пропуск обработки"
        skip_search:

        if (yii::$app->request->isPost) {
            // POST-запрос

            yii::$app->response->format = Response::FORMAT_JSON;

            return [
                'panel' => $this->renderPartial('/search/panel'),
                'hide' => (int) $auth_only,
                '_csrf' => yii::$app->request->getCsrfToken()
            ];
        } else {
            // GET-запрос

            return $this->render('/search/index');
        }
    }
}
