<?php

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\EventListener;

use CpCreation\VitiCore\AgeVerification\AgeVerificationProofManager;
use CpCreation\VitiCore\AgeVerification\Repository\AgeVerificationConfigRepository;
use CpCreation\VitiCore\AgeVerification\RobotRequestDetector;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Twig\Environment;

#[AsEventListener(event: KernelEvents::RESPONSE, method: 'onKernelResponse', priority: -32)]
final readonly class AgeVerificationResponseSubscriber
{
    /** @var list<string> */
    private const array EXCLUDED_PATH_PREFIXES = [
        '/_components',
        '/_profiler',
        '/_wdt',
        '/admincp',
        '/api',
        '/docs',
        '/robots.txt',
        '/sitemap',
        '/.well-known',
    ];

    public function __construct(
        private AgeVerificationConfigRepository $configRepository,
        private AgeVerificationProofManager $proofManager,
        private RobotRequestDetector $robotRequestDetector,
        private Environment $twig,
    ) {}

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

        $request = $event->getRequest();
        $response = $event->getResponse();
        $rememberForDays = $request->attributes->get(AgeVerificationProofManager::REQUEST_REMEMBER_DAYS);
        $configFingerprint = $request->attributes->get(AgeVerificationProofManager::REQUEST_CONFIG_FINGERPRINT);
        if (\is_int($rememberForDays) && \is_string($configFingerprint)) {
            $response->headers->setCookie($this->proofManager->createCookie(
                $request,
                $rememberForDays,
                $configFingerprint,
            ));

            return;
        }

        if (!$this->isEligibleHtmlResponse($request, $response)) {
            return;
        }

        $config = $this->configRepository->findOne();
        if (null === $config || !$config->isEnabled()) {
            return;
        }

        $response->setVary(['Cookie', 'User-Agent'], false);
        if ($this->robotRequestDetector->isRobot($request)) {
            return;
        }

        $response->setPrivate();
        $response->headers->addCacheControlDirective('no-store');
        if ($this->proofManager->isVerified($request, $config->getProofFingerprint())) {
            return;
        }

        $content = $response->getContent();
        if (!\is_string($content)) {
            return;
        }

        $closingBodyPosition = strripos($content, '</body>');
        if (false === $closingBodyPosition) {
            return;
        }

        $ageGate = $this->twig->render('@CpCreationVitiCore/age_verification/_mount.html.twig');
        $response->setContent(substr_replace($content, $ageGate . "\n", $closingBodyPosition, 0));
    }

    private function isEligibleHtmlResponse(Request $request, Response $response): bool
    {
        if (Request::METHOD_GET !== $request->getMethod() || Response::HTTP_OK !== $response->getStatusCode()) {
            return false;
        }

        foreach (self::EXCLUDED_PATH_PREFIXES as $excludedPathPrefix) {
            if (str_starts_with($request->getPathInfo(), $excludedPathPrefix)) {
                return false;
            }
        }

        $contentType = mb_strtolower((string) $response->headers->get('Content-Type'));

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