<?php

namespace Snaju\Inception\Http\Middleware;

use App\Middleware\Middleware;
use Snaju\Inception\Http\Request\WebRequest;

class MiddlewareRegistry
{

    private array $layers;

    public bool $isInitialized = false;

    public bool $allowGlobalMiddleware;

    private function loadMiddleware(array $array): array
    {
        $middlewareLayers = [];

        foreach ($array as $key => $value) {
            $middlewareLayers[] = new $value();
        }

        return $middlewareLayers;
    }

    private function createCoreFunction(\Closure $core): \Closure
    {
        return function($object) use($core) {
            return $core($object);
        };
    }

    private function createLayer($nextLayer, $layer): \Closure
    {
        return function($object) use($nextLayer, $layer){
            return $layer->process($object, $nextLayer);
        };
    }

    public function __construct(array $layers = [], $allowGlobal = true)
    {
        $this->allowGlobalMiddleware = $allowGlobal;
        $this->layers = $layers;
    }

    public function init($routeMiddlewareLayers = [], $cancelGlobal = false): MiddlewareRegistry
    {
        $layers = [];
        $this->allowGlobalMiddleware = !$cancelGlobal;

        // First load all the global middlewareRegistry (unless global middlewareRegistry loading canceled)
        if ($this->allowGlobalMiddleware) {
            // Load the class App\Middleware\Middleware.php
            $classVars = get_class_vars(get_class(new Middleware));
            foreach ($classVars as $name => $value) {
                switch ($name) {
                    case 'global':
                        $layers = $this->loadMiddleware($value);
                        break;
    //                case 'route':
                        //
    //                    break;
                }
            }
        }

        // Next load any middlewareRegistry that has been set to run for the route
        $layers = array_merge($layers, $this->loadMiddleware($routeMiddlewareLayers));
        $this->isInitialized = true;
        return new static(array_merge($this->layers, $layers));
    }

    public function process(WebRequest $request, \Closure $core)
    {
        $coreFunction = $this->createCoreFunction($core);

        $layers = array_reverse($this->layers);

        $complete = array_reduce($layers, function($nextLayer, $layer){
            return $this->createLayer($nextLayer, $layer);
        }, $coreFunction);

        // We now have all the layers of the application like an "onion" and
        // can start passing the object down to each layer.
        return $complete($request);
    }

    public function toArray(): array
    {
        return $this->layers;
    }

}