<?php

namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;

class CsrfProtectionSubscriber implements EventSubscriberInterface
{
    private const EXCLUDED_ROUTES = [
        'app_login',
        'app_logout',
        'app_stripe_webhook',
        'app_stripe_webhook_connect',
        'app_order_create',
        'app_order_create_intent',
        'app_account_event_reorder_categories',
        'app_account_event_reorder_billets',
        'app_account_event_save_billet_design',
    ];

    private const TOKEN_ID = 'form_protection';
    private const TOKEN_FIELD = '_csrf_token';

    public function __construct(
        private CsrfTokenManagerInterface $csrfTokenManager,
    ) {
    }

    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::REQUEST => ['onKernelRequest', 5],
            KernelEvents::RESPONSE => ['onKernelResponse', -10],
        ];
    }

    public function onKernelRequest(RequestEvent $event): void
    {
        if (!$event->isMainRequest()) {
            return;
        }

        $request = $event->getRequest();

        if (!$this->shouldVerifyCsrf($request)) {
            return;
        }

        $token = $request->request->getString(self::TOKEN_FIELD);
        if ('' !== $token && !$this->csrfTokenManager->isTokenValid(new CsrfToken(self::TOKEN_ID, $token))) {
            $event->setResponse(new Response('CSRF token invalid.', 403));
        }
    }

    private function shouldVerifyCsrf(\Symfony\Component\HttpFoundation\Request $request): bool
    {
        if (!$request->isMethod('POST')) {
            return false;
        }

        if (\in_array($request->attributes->getString('_route'), self::EXCLUDED_ROUTES, true)) {
            return false;
        }

        return !str_contains($request->headers->get('Content-Type', ''), 'application/json');
    }

    public function onKernelResponse(ResponseEvent $event): void
    {
        if (!$event->isMainRequest()) {
            return;
        }

        $response = $event->getResponse();
        $contentType = $response->headers->get('Content-Type', '');

        if (!str_contains($contentType, 'text/html')) {
            return;
        }

        $content = $response->getContent();
        if (!$content || !str_contains($content, 'method="post"')) {
            return;
        }

        $token = $this->csrfTokenManager->getToken(self::TOKEN_ID)->getValue();
        $hiddenField = '<input type="hidden" name="'.self::TOKEN_FIELD.'" value="'.$token.'">';

        $content = str_replace('method="post">', 'method="post">'.$hiddenField, $content);
        $response->setContent($content);
    }
}
