<?php

use intec\Core;
use intec\core\base\InvalidParamException;
use intec\core\handling\controllers\Router;
use intec\core\helpers\Encoding;
use intec\core\helpers\Json;
use intec\core\helpers\Type;
use intec\core\web\NotFoundHttpException;
use intec\core\web\Response;
use intec\core\web\UploadedFile;
use intec\template\Template;

// Проверяем необходимые переменные запроса
if (!isset($_GET['siteId']) || !isset($_GET['templateId'])) {
    http_response_code(404);

    return;
}

$requestedSiteId = trim((string)$_GET['siteId']);
$requestedTemplateId = trim((string)$_GET['templateId']);

foreach ([$requestedSiteId, $requestedTemplateId] as $requestedSegment) {
    if ($requestedSegment === ''
        || mb_strlen($requestedSegment) > 64
        || !preg_match('/^[a-zA-Z0-9_.-]+$/', $requestedSegment)) {
        http_response_code(404);

        return;
    }
}

const STOP_STATISTICS = true;

// Подключаем стандартный пролог CMS (контекст сайта — по домену/мультисайту)
require_once $_SERVER['DOCUMENT_ROOT'].'/bitrix/modules/main/include/prolog_before.php';

$context = \Bitrix\Main\Context::getCurrent();
$ctxLid = $context?->getSite();
$siteFromContext = ($ctxLid !== null && (string)$ctxLid !== '') ? (string)$ctxLid : null;
$siteFromConstant = (defined('SITE_ID') && (string)SITE_ID !== '') ? (string)SITE_ID : null;

if ($siteFromContext !== null && $siteFromConstant !== null && $siteFromContext !== $siteFromConstant) {
    http_response_code(404);

    return;
}

$currentSiteId = $siteFromConstant ?? $siteFromContext;

// siteId из GET = LID текущего сайта (ядро или контекст; при обоих — без расхождений)
if ($currentSiteId === null || $currentSiteId === '' || $requestedSiteId !== $currentSiteId) {
    http_response_code(404);

    return;
}

$siteRow = \Bitrix\Main\SiteTable::getRow([
    'filter' => [
        '=LID' => $requestedSiteId,
        '=ACTIVE' => 'Y',
    ],
    'select' => ['LID'],
]);

if ($siteRow === null) {
    http_response_code(404);

    return;
}

$templateRow = \Bitrix\Main\SiteTemplateTable::getRow([
    'filter' => [
        '=SITE_ID' => $requestedSiteId,
        '=TEMPLATE' => $requestedTemplateId,
    ],
    'select' => ['ID'],
]);

if ($templateRow === null) {
    http_response_code(404);

    return;
}

foreach (['SITE_ID' => $requestedSiteId, 'SITE_TEMPLATE_ID' => $requestedTemplateId] as $name => $value) {
    if (!defined($name)) {
        define($name, $value);
    } elseif ((string)constant($name) !== $value) {
        http_response_code(404);

        return;
    }
}

// Подключаем загрузчик шаблона — уже после согласованных SITE_ID / SITE_TEMPLATE_ID
require_once __DIR__.'/parts/loader.php';

$request = Core::$app->request;
// Получаем маршрут из GET параметров запроса
$route = $request->get('route');
$route = Encoding::convert($route, null, Encoding::UTF8);

/** @var ?Response $response */
$response = null;

$requestExposeExceptionDetails = static function (): bool {
    $cfg = \Bitrix\Main\Config\Configuration::getValue('exception_handling');

    return is_array($cfg) && key_exists('debug', $cfg) && $cfg['debug'] === true;
};

// Выполняем скрипт кастомного начала запроса
Template::getInstance()->loadScript('parts/custom/request.start.php', [
    'response' => &$response
]);

if (!($response instanceof Response)) {
    // Пакетная обработка действий, если это POST, AJAX и страница не установлена (пример запроса: request.php?actions[0][route]=basket.add&actions[0][parameters][id]=1&siteId=s1&templateId=kosmos)
    if (empty($route) && $request->getIsPost() && $request->getIsAjax()) {
        if (!check_bitrix_sessid()) {
            CHTTP::SetStatus('403 Forbidden');
            $response = new Response([
                'data' => Encoding::convert([[
                    'code' => 'csrf',
                    'data' => null,
                    'message' => 'CSRF validation failed',
                    'status' => 'error'
                ]], Encoding::UTF8, Encoding::getDefault()),
                'format' => Response::FORMAT_JSON
            ]);
        } else {
            $actions = $request->post('actions');

            // Если действия пришли
            if (is_array($actions) && count($actions) < 21){
                $data = [];
                $router = new Router();
                $router->addition = 'Actions';
                $router->namespace = '\\intec\\template\\actions';
                $router->separator = '.';

                // В пакетном режиме обрабатываем каждое действие с помощью роутера
                foreach ($actions as $index => $action) {
                    // Получаем маршрут действия
                    $route = $action['route'] ?? null;
                    $route = Encoding::convert($route, null, Encoding::UTF8);
                    // Получаем параметры действия
                    $parameters = $action['parameters'] ?? [];

                    // Если маршрут не указан, указываем, что действие было некорректным (badAction)
                    if (empty($route) && !Type::isNumeric($route)) {
                        $data[] = [
                            'code' => 'badAction',
                            'data' => null,
                            'message' => 'Bad action',
                            'status' => 'error'
                        ];

                        continue;
                    }

                    // Если параметры пришли, необходимо преобразовать их из JSON
                    if (!empty($parameters)) {
                        try {
                            // Производим расшифровку JSON в массив
                            $parameters = Json::decode($parameters);
                            // Производим конвертацию кодировки из UTF-8 в текущую кодировку сайта
                            $parameters = Encoding::convert($parameters, null, Encoding::UTF8);
                        } catch (InvalidParamException $exception) {
                            $parameters = [];
                        }
                    } else {
                        $parameters = [];
                    }

                    try {
                        // Пробуем маршрутизировать действие
                        $route = $router->resolve($route);

                        // Если маршрут был найден
                        if ($route !== null) {
                            // Если параметр файлов пустой, вставим туда файлы из запроса (если они есть в запросе), для доступа к ним
                            if (!isset($parameters['files'])) {
                                $parameters['files'] = UploadedFile::getInstancesByName('actions[' . $index . '][files]');

                                if (empty($parameters['files']))
                                    unset($parameters['files']);
                            }

                            // Запускаем действие из маршрута с параметрами
                            $data[] = $route->run($parameters);
                        } else {
                            // Если не нашли маршрут, указываем на ошибку поиска маршрута (unresolvedAction)
                            $data[] = [
                                'code' => 'unresolvedAction',
                                'data' => [
                                    'route' => $route,
                                    'parameters' => $parameters
                                ],
                                'message' => 'Unresolved action',
                                'status' => 'error'
                            ];
                        }
                    } catch (Exception $exception) {
                        // Если возникло исключение во время работы действия, сообщаем об этом (actionException)
                        $data[] = [
                            'code' => 'actionException',
                            'data' => [
                                'parameters' => $parameters
                            ],
                            'message' => 'Action exception',
                            'status' => 'error'
                        ];
                    }
                }

                // Устанавливаем ответ в формате JSON.
                $response = new Response([
                    'data' => Encoding::convert($data, Encoding::UTF8, Encoding::getDefault()),
                    'format' => Response::FORMAT_JSON
                ]);
            }
        }
    } else if (!empty($route)) {
        if (!check_bitrix_sessid()) {
            CHTTP::SetStatus('403 Forbidden');
            $response = new Response([
                'data' => Encoding::convert([[
                    'code' => 'csrf',
                    'data' => null,
                    'message' => 'CSRF validation failed',
                    'status' => 'error'
                ]], Encoding::UTF8, Encoding::getDefault()),
                'format' => Response::FORMAT_JSON
            ]);
        } else {
            // Если маршрут установлен (пример запроса: request.php?route=component.show&siteId=s1&templateId=kosmos)
            // Получаем данные и конвертируем их из кодировки UTF-8 в текущую кодировку сайта
            $data = $request->get();
            $data = Encoding::convert($data, null, Encoding::UTF8);

            unset($data['route']);
            unset($data['siteId']);
            unset($data['templateId']);

            $router = new Router();
            $router->namespace = '\\intec\\template\\controllers';
            $router->separator = '.';

            try {
                // Пробуем маршрутизировать маршрут
                $route = $router->resolve($route);

                // Если маршрут был найден, запускаем страницу
                if ($route !== null) {
                    $response = $route->run($data);
                } else {
                    // Иначе отвечаем, что маршрут не был найден
                    $response = new Response([
                        'format' => Response::FORMAT_RAW
                    ]);

                    $response->setStatusCodeByException(new NotFoundHttpException());
                }
            } catch (Exception $exception) {
                $response = new Response([
                    'data' => $requestExposeExceptionDetails()
                        ? $exception->getMessage() . ': ' . $exception->getTraceAsString()
                        : 'Request processing error.',
                    'format' => Response::FORMAT_RAW
                ]);

                $response->setStatusCodeByException($exception);
            }

            // Если выходные данные не являются экземпляром ответа, поместим их в него и установим тип RAW (без обработки)
            if (!($response instanceof Response)) {
                $response = new Response([
                    'data' => $response,
                    'format' => Response::FORMAT_RAW
                ]);
            }
        }
    }
}

// Выполняем скрипт кастомного окончания запроса
Template::getInstance()->loadScript('parts/custom/request.end.php', [
    'response' => &$response
]);

// Если выходные данные не являются экземпляром ответа, значит возникла какая-то ошибка в процессе
if (!($response instanceof Response)) {
    $response = new Response([
        'format' => Response::FORMAT_RAW
    ]);

    $response->setStatusCodeByException(new NotFoundHttpException());
}

// Отправим ответ
$response->send();
