<?php declare(strict_types=1);

namespace Lean;

use Dotenv\Dotenv;

use DI\ContainerBuilder,
    DI\Container;

use League\Route\Strategy\ApplicationStrategy;

use Psr\Http\Message\ServerRequestInterface;

use Ulmus\Container\AdapterProxy;

use Zend\Diactoros\ServerRequestFactory,
    Zend\HttpHandlerRunner\Emitter\EmitterInterface;

class Kernel {

    protected Container $container;

    protected string $locale;

    public array $paths = [];

    public int $errorReporting = E_ALL & ~E_USER_DEPRECATED & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE;

    public string $definitionFilePath;

    public string $errorLogPath;

    public string $projectPath;

    public string $routeDefinitionList = 'routes.list';

    public function __construct(string $projectPath)
    {
        $this->projectPath = $projectPath;

        $this->setEnvironment();
        $this->initializeEngine();
        $this->setupContainer();
        $this->serviceContainer();
        $this->handleRequest();
    }

    public static function putenv($var, $value)
    {
        putenv("$var=$value");
        $_ENV[$var] = $value;
    }

    protected function setEnvironment() {
        static::putenv('PROJECT_PATH', $this->projectPath);

        // Environment vars (accessible from \DI\env(), getenv(), $_ENV and $_SERVER)
        Dotenv::create(getenv("PROJECT_PATH"))->load();

        // Paths and directories
        foreach($this->paths as $name => $envkey) {
            static::putenv($name, realpath(getenv("PROJECT_PATH") . DIRECTORY_SEPARATOR . getenv($envkey)));
        }

        // Override using headers
        foreach(['APP_ENV', 'DEBUG', ] as $env) {
            if ( null !== $value = $_SERVER["HTTP_$env"] ?? null ) {
                static::putenv($env, $value);
            }
        }
    }

    protected function initializeEngine() : self
    {
        # Defining default locale and timezone
        date_default_timezone_set(getenv("DEFAULT_TIMEZONE"));

        setlocale(LC_ALL, $this->locale = getenv("DEFAULT_LOCAL"));
        setlocale(LC_TIME, getenv("DEFAULT_TIME"), getenv("DEFAULT_TIME_FALLBACK"));

        ini_set("log_errors", "1");
        ini_set("error_log", $this->errorLogPath);
        ini_set('display_errors', getenv("DEBUG") ? 'on' : 'off');

        error_reporting($this->errorReporting);

        return $this;
    }

    protected function setupContainer() : self
    {
        $containerBuilder = new ContainerBuilder();

        if (getenv("APP_ENV") === "prod") {
            if (getenv("CACHE_PATH")) {
                $containerBuilder->enableCompilation(getenv("CACHE_PATH") . "/di/");
                $containerBuilder->writeProxiesToFile(true);
            }
        }

        $containerBuilder->useAnnotations(false);

        if ($this->definitionFilePath ?? false) {
            $containerBuilder->addDefinitions(require($this->definitionFilePath));
        }

        $this->container = $containerBuilder->build();

        return $this;
    }

    protected function serviceContainer() : self
    {
        $this->container->has(AdapterProxy::class) and $this->container->get(AdapterProxy::class);

        return $this;
    }

    protected function handleRequest() : self
    {
        ServerRequestFactory::fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES);

        // Router
        $routeFactory = $this->container->get($this->routeDefinitionList);

        $router = $routeFactory($this->container);

        $router->setStrategy($this->container->get(ApplicationStrategy::class));

        // Handle requests
        $response = $router->dispatch($this->container->get(ServerRequestInterface::class));

        // Reply to clients
        $this->container->get(EmitterInterface::class)->emit($response);

        return $this;
    }
};