<?php
/**
 * Copyright since 2025 InPost S.A.
 *
 * For the full license information, please view the LICENSE file bundled with the module.
 *
 * @author InPost S.A.
 * @copyright since 2025 InPost S.A.
 * @license MIT
 */

declare(strict_types=1);

use InPost\International\Controller\Front\Checkout\FindClosestPointController;
use InPost\International\Controller\Front\Checkout\UpdateDeliveryOptionsController;
use InPost\International\Controller\Front\Checkout\ValidatePhoneNumberController;
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;

if (!defined('_PS_VERSION_')) {
    exit;
}

class InPostInternationalCheckoutModuleFrontController extends ModuleFrontController
{
    private const ACTION_MAP = [
        'updateDeliveryOptions' => [
            'controller' => UpdateDeliveryOptionsController::class,
            'methods' => ['POST'],
        ],
        'findClosestPoint' => [
            'controller' => FindClosestPointController::class,
            'methods' => ['POST'],
        ],
        'validatePhoneNumber' => [
            'controller' => ValidatePhoneNumberController::class,
            'methods' => ['POST'],
        ],
    ];

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

    /**
     * {@inheritDoc}
     */
    protected $content_only = true;

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

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

        $this->module->terminate($response);
    }

    private function handle(Request $request): Response
    {
        try {
            $controller = $this->resolveController($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 checkout request [{method} {uri}]: {exception}', [
                'method' => $request->getMethod(),
                'uri' => $request->getPathInfo() . '?' . $request->getQueryString(),
                'exception' => $e,
            ]);

            http_response_code(500);

            throw $e;
        }

        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 (!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);
        }

        $controller = $this->get($route['controller']);
        assert(is_callable($controller));

        return $controller;
    }
}
