<?php


namespace CubiStore\Web;

use CubiStore\Web\Utils\ICompiledRoutes;
use CubiStore\Web\Utils\RouteCompiler;
use DI\Bridge\Slim\Bridge;
use DI\Container;
use DI\ContainerBuilder;
use Slim\App;
use function DI\autowire;

class Main
{
    /**
     * @var string
     */
    private $env;

    /**
     * @var Container
     */
    private $container;

    /**
     * @var App
     */
    private $app;

    private function __construct(string $env = 'prod')
    {
        $this->env = $env;
    }

    public static function main(string $env = 'prod')
    {
        static::make($env)->run();
    }

    public static function make(string $env = 'prod')
    {
        $_ENV['ENV'] = $env;
        $main = new Main($env);
        $main->setup();
        return $main;
    }

    public function run()
    {
        $this->app->run();
    }

    public function setup()
    {
        $routes = $this->setupRoutes();
        $this->setupContainer($routes);
        $this->setupSlim($routes);
    }

    public function setupRoutes(): ICompiledRoutes
    {
        $compiledRoutesFile = __DIR__ . '/../var/cache/routes/CompiledRoutes.php';

        if ($this->env !== 'prod' || !file_exists($compiledRoutesFile)) {
            $routes = include(__DIR__ . '/../config/routes.php');
            $compiler = new RouteCompiler($compiledRoutesFile);
            $compiler->addRoutes($routes);
            $compiler->writeCache();
        }

        include(__DIR__ . '/../var/cache/routes/CompiledRoutes.php');
        $x = "\CubiStore\Web\Cache\CompiledRoutes";
        return new $x();
    }

    public function setupContainer(ICompiledRoutes $routes)
    {
        $containerBuilder = new ContainerBuilder();

        if ($this->env === 'prod') {
            $containerBuilder->enableCompilation(__DIR__ . '/../var/cache/container');
        }

        $containerBuilder->addDefinitions(include(__DIR__ . '/../config/container.php'));

        if ($this->env !== 'prod') {
            $def = [];
            foreach ($routes->getKeys() as $key) {
                if (class_exists($key)) {
                    $def[$key] = autowire();
                }
            }

            $containerBuilder->addDefinitions($def);
        }

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

    public function setupSlim(ICompiledRoutes $compiledRoutes)
    {
        $app = Bridge::create($this->container);
        $compiledRoutes->configure($app);
        $app->addRoutingMiddleware();
        $app->addBodyParsingMiddleware();
        $this->app = $app;
    }

    public function get(string $class)
    {
        return $this->container->get($class);
    }
}
