<?php
// middleware
namespace api\utils;

session_start();

use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Selective\SameSiteCookie\SameSiteCookieConfiguration;
use Selective\SameSiteCookie\SameSiteCookieMiddleware;
use Slim\App;
use Slim\Exception\HttpInternalServerErrorException;
use Slim\Exception\HttpMethodNotAllowedException;
use Slim\Exception\HttpNotFoundException;
use Slim\Psr7\Response;
use Tuupola\Middleware\JwtAuthentication;
use Tuupola\Middleware\JwtAuthentication\RequestMethodRule;
use Tuupola\Middleware\JwtAuthentication\RequestPathRule;

/**
 * Middleware
 * Define all middleware functions
 */
class middleware
{
    /**
     * Constructor for middleware
     * @param App $app - Slim App
     */
    public function __construct(App $app)
    {
        $this->baseMiddleware($app);
        $this->sameSiteConfig($app);
        $this->jwtAuth($app);
        $this->errorHandling($app);
        $this->returnAsJSON($app);
    }

    /**
     * Base middleware
     * @param App $app - Slim App
     */
    public function baseMiddleware(App $app): void
    {
        $app->addBodyParsingMiddleware();
        $app->addRoutingMiddleware();
    }
    
    /**
     * SameSite Cookie Configuration
     * @param App $app - Slim App
     */
    public function sameSiteConfig(App $app): void
    {
        $ssConfig = new SameSiteCookieConfiguration(["same_site" => "strict"]);
        $app->add(new SameSiteCookieMiddleware($ssConfig));
    }

    /**
     * Return all responses as JSON
     * @param App $app - Slim App
     */
    public function returnAsJSON(App $app): void
    {
        $app->add(function ($request, $handler)
        {
            $response = $handler->handle($request);
            return $response->withHeader("Content-Type", "application/json");
        });
    }
    
    /**
     * JWT Authentication
     * @param App $app - Slim App
     */
    public function jwtAuth(App $app): void
    {
        $jwtSecret = getSecretKey();
        $app->add(new JwtAuthentication([
            "rules" => [
                new RequestPathRule([
                    "path" => ["/api/projectData", "/api/timelineData/[a-z]*", "/api/projectImage/[0-9]*", "/api/logout"],
                    "ignore" => ["/api/contact", "/api/userData/login", "/api/userData/changePassword"]
                ]),
                new RequestMethodRule([
                    "ignore" => ["OPTIONS", "GET"]
                ])
            ],
            "secret" => $jwtSecret,
            "error" => function ($response)
            {
                session_destroy();
                $response->getBody()->write(json_encode(array("status" => "401", "message" =>
                    "Unauthorized, please provide a valid token")));
                return $response->withStatus(401);
            }
        ]));
    }

    /**
     * Error handling
     * @param App $app - Slim App
     */
    public function errorHandling(App $app): void
    {
        $app->add(function (ServerRequestInterface $request, RequestHandlerInterface $handler)
        {
            try
            {
                return $handler->handle($request);
            }
            catch (HttpNotFoundException $httpException)
            {
                $response = (new Response())->withStatus(404);
                $response->getBody()->write(json_encode(array("status" => "404", "message" => $request->getUri()->getPath() . " not found")));
                return $response;
            }
            catch (HttpMethodNotAllowedException $httpException)
            {
                $response = (new Response())->withStatus(405);
                $response->getBody()->write(json_encode(array("status" => "405", "message" => "Method not allowed")));
                return $response;
            }
            catch (HttpInternalServerErrorException $exception)
            {
                $response = (new Response())->withStatus(500);
                $response->getBody()->write(json_encode(array("status" => "500", "message" => $exception->getMessage())));
                return $response;
            }
        });
        $app->addErrorMiddleware(true, true, true);

    }
    
}