<?php
declare(strict_types=1);

use phpOMS\Autoloader;
use phpOMS\Application\ApplicationAbstract;
use phpOMS\Localization\ISO639x1Enum;
use phpOMS\Localization\Localization;
use phpOMS\Log\FileLogger;
use phpOMS\Message\Http\HttpRequest;
use phpOMS\Message\Http\HttpResponse;
use phpOMS\System\File\PathException;
use phpOMS\Uri\HttpUri;
use phpOMS\Uri\UriFactory;

class WebApplication extends ApplicationAbstract
{
    public function __construct(array $config)
    {
        $response = null;
        $sub      = null;

        try {
            $this->setupHandlers();

            $this->logger = FileLogger::getInstance($config['log']['file']['path'], false);

            UriFactory::setQuery('/prefix', '', true);
            UriFactory::setQuery('/backend', 'backend/', true);
            UriFactory::setQuery('/api', 'api/', true);
            $applicationName = $this->getApplicationName(HttpUri::fromCurrent(), $config['app'], $config['page']['root']);
            $request         = $this->initRequest($config['page']['root'], $config['app']);
            $response        = $this->initResponse($request, $config);

            $this->theme = $this->getApplicationTheme($request, $config['app']['domains']);

            $app = '\Applications\\' . $applicationName . '\Application';
            $sub = new $app($this, $config);
        } catch (\Throwable $e) {
            $this->logger->critical(FileLogger::MSG_FULL, [
                'message' => $e->getMessage(),
                'line'    => __LINE__, ]);
            $sub = new \Applications\E500\Application($this, $config);
        } finally {
            if ($sub === null) {
                $sub = new \Applications\E500\Application($this, $config);
            }

            if ($response === null) {
                $response = new HttpResponse();
            }

            $request ??= HttpRequest::createFromSuperglobals();
            $sub->run($request, $response);

            $body = $response->getBody(true);

            if (isset($this->sessionManager)) {
                $this->sessionManager->save();
            }

            if (!$response->header->isLocked()) {
                $response->header->push();
            }

            if (isset($this->sessionManager)) {
                $this->sessionManager->lock();
            }

            echo $body;
        }
    }

    private function setupHandlers() : void
    {
        \set_exception_handler(['\phpOMS\UnhandledHandler', 'exceptionHandler']);
        \set_error_handler(['\phpOMS\UnhandledHandler', 'errorHandler']);
        \register_shutdown_function(['\phpOMS\UnhandledHandler', 'shutdownHandler']);
        \mb_internal_encoding('UTF-8');
    }

    private function initRequest(string $rootPath, array $config) : HttpRequest
    {
        $request     = HttpRequest::createFromSuperglobals();
        $subDirDepth = \substr_count($rootPath, '/') - 1;

        $defaultLang = $config['domains'][$request->uri->host]['lang'] ?? $config['default']['lang'];
        $uriLang     = \strtolower($request->uri->getPathElement($subDirDepth + 0));
        $requestLang = $request->getRequestLanguage();
        $langCode    = ISO639x1Enum::isValidValue($uriLang)
            ? $uriLang
            : (ISO639x1Enum::isValidValue($requestLang)
                ? $requestLang
                : $defaultLang
            );

        $pathOffset = $subDirDepth
            + (ISO639x1Enum::isValidValue($uriLang)
                ? 1 + ($this->getApplicationNameFromString($request->uri->getPathElement($subDirDepth + 1)) !== 'E500' ? 1 : 0)
                : 0 + ($this->getApplicationNameFromString($request->uri->getPathElement($subDirDepth + 0)) !== 'E500' ? 1 : 0)
            );

        $request->createRequestHashs($pathOffset);
        $request->uri->setRootPath($rootPath);
        $request->uri->setPathOffset($pathOffset);
        UriFactory::setupUriBuilder($request->uri);

        $request->header->l11n->loadFromLanguage($langCode, \explode('_', $request->getLocale())[1] ?? '*');

        return $request;
    }

    private function initResponse(HttpRequest $request, array $config) : HttpResponse
    {
        $response = new HttpResponse(new Localization());
        $response->header->set('content-type', 'text/html; charset=utf-8');
        $response->header->set('x-xss-protection', '1; mode=block');
        $response->header->set('x-content-type-options', 'nosniff');
        $response->header->set('x-frame-options', 'SAMEORIGIN');
        $response->header->set('referrer-policy', 'same-origin');

        if ($request->isHttps()) {
            $response->header->set('strict-transport-security', 'max-age=31536000');
        }

        $defaultLang = $config['app']['domains'][$request->uri->host]['lang'] ?? $config['app']['default']['lang'];

        $uriLang = \strtolower($request->uri->getPathElement(0));
        $uriLang = ISO639x1Enum::isValidValue($uriLang) && \in_array($uriLang, $config['language'])
            ? $uriLang
            : \strtolower($request->uri->getPathElement(0, false));

        $requestLang = $request->getLanguage();
        $langCode    = ISO639x1Enum::isValidValue($uriLang) && \in_array($uriLang, $config['language'])
            ? $uriLang
            : (ISO639x1Enum::isValidValue($requestLang) && \in_array($requestLang, $config['language'])
                ? $requestLang
                : $defaultLang
            );

        $response->header->l11n->loadFromLanguage($langCode, \explode('_', $request->getLocale())[1] ?? '*');
        UriFactory::setQuery('/lang', $request->getLanguage(), true);

        if (ISO639x1Enum::isValidValue($uriLang)) {
            UriFactory::setQuery('/prefix',  $uriLang . '/' . (empty(UriFactory::getQuery('/prefix')) ? '' : UriFactory::getQuery('/prefix')), true);
            UriFactory::setQuery('/api',  $uriLang . '/' . (empty(UriFactory::getQuery('/api')) ? '' : UriFactory::getQuery('/api')), true);
        }

        return $response;
    }

    private function getApplicationName(HttpUri $uri, array $config, string $rootPath) : string
    {
        $subDirDepth = \substr_count($rootPath, '/') - 1;

        // check subdomain
        $appName = $uri->getSubdomain();
        $appName = $this->getApplicationNameFromString($appName);

        if ($appName !== 'E500') {
            return $appName;
        }

        // check uri path 0 (no language is defined)
        $appName = $uri->getPathElement($subDirDepth + 0);
        $appName = $this->getApplicationNameFromString($appName);

        if ($appName !== 'E500') {
            UriFactory::setQuery(
                '/prefix',
                empty(UriFactory::getQuery('/prefix')
                    ? ''
                    : UriFactory::getQuery('/prefix') . '/') . $uri->getPathElement($subDirDepth + 1) . '/',
                true
            );

            return $appName;
        }

        // check uri path 1 (language is defined)
        if (ISO639x1Enum::isValidValue($uri->getPathElement($subDirDepth + 0))) {
            $appName = $uri->getPathElement($subDirDepth + 1);
            $appName = $this->getApplicationNameFromString($appName);

            if ($appName !== 'E500') {
                UriFactory::setQuery(
                    '/prefix',
                    empty(UriFactory::getQuery('/prefix')
                        ? ''
                        : UriFactory::getQuery('/prefix') . '/') . $uri->getPathElement($subDirDepth + 1) . '/',
                    true
                );

                return $appName;
            }
        }

        // check config
        $appName = $config['domains'][$uri->host]['app'] ?? $config['default']['app'];

        return $this->getApplicationNameFromString($appName);
    }

    private function getApplicationNameFromString(string $app) : string
    {
        $applicationName = \ucfirst(\strtolower($app));

        if (empty($applicationName) || !Autoloader::exists('\\Applications\\' . $applicationName . '\\Application')) {
            $applicationName = 'E500';
        }

        return $applicationName;
    }

    private function getApplicationTheme(HttpRequest $request, array $config) : string
    {
        return $config[$request->uri->host]['theme'] ?? 'Backend';
    }

    public function loadLanguageFromPath(string $language, string $path) : void
    {
        /* Load theme language */
        if (($absPath = \realpath($path)) === false) {
            throw new PathException($path);
        }

        /** @noinspection PhpIncludeInspection */
        $themeLanguage = include $absPath;
        $this->l11nManager->loadLanguage($language, '0', $themeLanguage);
    }
}
