<?php

declare(strict_types=1);

namespace apexl\Io\modules\system\hooks;

use apexl\Config\ConfigurationInterface;
use apexl\Config\Exceptions\MissingConfigException;
use apexl\Io\interfaces\AbstractHook;
use apexl\Io\middleware\AddCorsHeaders;
use apexl\Io\middleware\AddRouteToHttpPaths;
use apexl\Io\modules\system\collections\MiddlewareCollectionCollection;
use apexl\Io\modules\system\exceptions\WhoopsSetupException;
use apexl\Io\modules\system\services\MiddlewareCollector;
use apexl\Io\modules\system\services\WhoopsHandlerCollector;
use app\vendor\apexl\io\src\Io\middleware\AddBuildHeader;
use app\vendor\apexl\io\src\Io\middleware\InjectIoRequest;
use app\vendor\apexl\io\src\Io\middleware\RequireSsl;
use Slim\App;
use Slim\Middleware\BodyParsingMiddleware;
use Slim\Middleware\MethodOverrideMiddleware;
use Slim\Middleware\RoutingMiddleware;
use Zeuxisoo\Whoops\Slim\WhoopsMiddleware;

final readonly class AddMiddleware implements AbstractHook
{
    public function __construct(
        private App $application,
        private WhoopsHandlerCollector $whoopsHandlerCollector,
        private ConfigurationInterface $config,
    ) {}

    public function hooks(): array
    {
        return [MiddlewareCollector::HOOK__COLLECT];
    }

    /**
     * @throws MissingConfigException
     * @throws WhoopsSetupException
     */
    public function __invoke(MiddlewareCollectionCollection $weightedMiddleware): MiddlewareCollectionCollection
    {
        $routingMiddleware = new RoutingMiddleware(
            $this->application->getRouteResolver(),
            $this->application->getRouteCollector()->getRouteParser()
        );

        $weightedMiddleware->add(AddCorsHeaders::class, weight: MiddlewareCollector::WEIGHT__FIRST);
        if ($this->config->get('system.require_ssl')) {
            $weightedMiddleware->add(RequireSsl::class, weight: MiddlewareCollector::WEIGHT__FIRST);
        }
        $weightedMiddleware->add(BodyParsingMiddleware::class, weight: MiddlewareCollector::WEIGHT__FIRST);
        $this->maybeAddWhoopsMiddleware($weightedMiddleware);
        $weightedMiddleware->add(MethodOverrideMiddleware::class, weight: MiddlewareCollector::WEIGHT__FIRST);
        $weightedMiddleware->add($routingMiddleware, weight: MiddlewareCollector::WEIGHT__FIRST);
        $weightedMiddleware->add(InjectIoRequest::class, weight: MiddlewareCollector::WEIGHT__FIRST);
        $weightedMiddleware->add(AddRouteToHttpPaths::class, weight: MiddlewareCollector::WEIGHT__FIRST);

        $weightedMiddleware->add(AddBuildHeader::class, weight: MiddlewareCollector::WEIGHT__LAST);


        return $weightedMiddleware;
    }

    /**
     * @throws MissingConfigException
     * @throws WhoopsSetupException
     */
    private function maybeAddWhoopsMiddleware(MiddlewareCollectionCollection $weightedMiddleware): void
    {
        if ($this->config->get('whoops.enable')) {
            $handlers = $this->whoopsHandlerCollector->collect();

            $weightedMiddleware->add(new WhoopsMiddleware(handlers: $handlers));
        }
    }
}
