<?php

use Gls\GlsPoland\Controller\Front\CheckoutController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;

class GlsPolandCheckoutModuleFrontController extends ModuleFrontController
{
    private const ACTION_MAP = [
        'selectParcelShop' => [
            'controller' => [CheckoutController::class, 'selectParcelShop'],
            'methods' => ['POST'],
        ],
    ];

    public $content_only = true;

    /**
     * @var GlsPoland
     */
    public $module;

    /**
     * @return never-returns
     */
    public function postProcess(): void
    {
        $request = $this->module->getCurrentRequest();

        $response = $this->handle($request);
        $response->send();

        exit;
    }

    private function handle(Request $request): Response
    {
        try {
            $controller = $this->resolveController($request);
            $this->validateCsrfToken($request);

            $response = ($controller)($request);
        } catch (HttpExceptionInterface $e) {
            $response = new JsonResponse([
                'error' => $e->getMessage(),
            ], $e->getStatusCode(), $e->getHeaders());
        } catch (Throwable $e) {
            $this->module->getLogger()->critical('An error occurred while processing the request [{method} {uri}]: {exception}', [
                'method' => $request->getMethod(),
                'uri' => $request->getRequestUri(),
                'exception' => $e,
            ]);

            http_response_code(500);

            throw $e;
        }

        $response->prepare($request);
        $this->context->cookie->write();

        return $response;
    }

    private function resolveController(Request $request): callable
    {
        $action = (string) $request->query->get('action');

        if (null === $route = self::ACTION_MAP[$action] ?? null) {
            throw new NotFoundHttpException(sprintf('No route found for "%s %s".', $request->getMethod(), $request->getRequestUri()));
        }

        if (isset($route['methods']) && !in_array($request->getMethod(), $route['methods'], true)) {
            $message = sprintf('No route found for "%s %s": Method Not Allowed (Allow: %s).', $request->getMethod(), $request->getRequestUri(), implode(', ', $route['methods']));

            throw new MethodNotAllowedHttpException($route['methods'], $message);
        }

        [$class, $method] = $route['controller'];

        return [$this->get($class), $method];
    }

    private function validateCsrfToken(Request $request): void
    {
        $token = $request->headers->get('X-CSRF-Token');

        if (null === $token || $token !== Tools::getToken(false)) {
            throw new UnprocessableEntityHttpException('CSRF token is invalid.');
        }
    }
}
