<?php

// JWTAuthMiddleware.php
namespace App\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as Handler;
use Slim\Psr7\Response;

use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\BeforeValidException;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
use JsonException;

use DateTime;

class JWTAuthMiddleware {

    private $secretKey;

    public function __construct($secretKey) {
        $this->secretKey = $secretKey;
    }

    public function __invoke(Request $request, Handler $handler): ResponseInterface {

        $token = $this->GetTokenFromHeaders($request);

        if (!$token) {
            return $this->ErrResponse(401, 'Token is no valid');
        }

        try {

            $decoded = JWT::decode($token, new Key($this->secretKey, 'HS256'));
            $request = $request->withAttribute('token', $decoded);

            return $handler->handle($request);
        } catch (ExpiredException $e) {
            return $this->ErrResponse(401, 'The token has expired');
        } catch (JsonException $e) {
            return $this->ErrResponse(400, 'The token is invalid');
        } catch (SignatureInvalidException $e) {
            return $this->ErrResponse(401, 'The token signature is not valid');
        } catch (Exception $e) {
            return $this->ErrResponse(401, 'Access denied');
        }
    }

    private function GetTokenFromHeaders(Request $request): ?string {

        $header = $request->getHeaderLine('Authorization');

        if(preg_match('/Bearer\s(\S+)/', $header, $matches)) {
            return $matches[1];
        }

        return null;
    }

    private function ErrResponse(int $statusCode, string $sms): ResponseInterface {

        $date = new DateTime();

        $payload = [
            'error' => $sms,
            'status' => $statusCode,
            'date' => $date->format('Y-m-d H:i:s')
        ];

        $response = new Response;
        $response->getBody()->write(json_encode($payload));

        return $response
            ->withHeader('Content-Type', 'application/json')
            ->withStatus($statusCode);
    }
}