<?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 Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Contracts\Translation\TranslatorInterface;

#[AsEventListener(event: KernelEvents::REQUEST, method: 'onKernelRequest')]
final readonly class RecaptchaFormSubscriber
{
    public function __construct(
        private string $reCaptchaKeyPrivate,
        private string $reCaptchaKeyPublic,
        private bool $reCaptchaEnterprise,
        private string $reCaptchaProject,
        private RequestStack $requestStack,
        private TranslatorInterface $translator,
    ) {
    }

    private function getSession(): ?SessionInterface
    {
        return $this->requestStack->getSession();
    }

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

        $request = $event->getRequest();

        $controller = (string) $request->attributes->get('_controller', '');

        if (
            (('POST' !== $request->getMethod()) && ($request->request->count() > 0))
            || (1 === mb_strpos($request->getPathInfo(), 'request'))
            || 'shop' === $request->attributes->get('_route')
            || 'payment' === $request->attributes->get('_route')
            || preg_match('/Controller\\\\Admin/', $controller)
            || 'turbo_stream' === $request->getPreferredFormat()
        ) {
            return;
        }

        if ($request->request->get('g-recaptcha-response')) {
            return;
        }

        foreach ($request->request->all() as $key => $form) {
            if (!\is_array($form)) {
                continue;
            }

            if (!\array_key_exists('g_recaptcha_response', $form) || '' === $form['g_recaptcha_response']) {
                $this->getSession()?->getFlashBag()->add(
                    'error',
                    $this->translator->trans('form.error-recaptcha', [], 'validators')
                );
                $event->setResponse(new RedirectResponse($request->getUri()));

                return;
            }

            $token = (string) $form['g_recaptcha_response'];
            $action = $key.'_g_recaptcha_response';

            $isValid = $this->reCaptchaEnterprise
                ? $this->validateEnterprise($request, $token, $action, $errorMessage)
                : $this->validateV3($request, $token, $errorMessage);

            if ($isValid) {
                return;
            }

            $this->getSession()?->getFlashBag()->add(
                'error',
                $errorMessage ?: $this->translator->trans('form.error-recaptcha', [], 'validators')
            );

            $event->setResponse(new RedirectResponse($request->getUri()));

            return;
        }
    }

    private function validateEnterprise($request, string $token, string $action, ?string &$errorMessage = null): bool
    {
        $errorMessage = null;

        $ch = curl_init();
        curl_setopt(
            $ch,
            \CURLOPT_URL,
            "https://recaptchaenterprise.googleapis.com/v1/projects/{$this->reCaptchaProject}/assessments?key={$this->reCaptchaKeyPrivate}"
        );
        curl_setopt($ch, \CURLOPT_POST, 1);

        $data = [
            'event' => [
                'token' => $token,
                'siteKey' => $this->reCaptchaKeyPublic,
                'userAgent' => $request->headers->get('User-Agent'),
                'userIpAddress' => $request->getClientIp(),
                'expectedAction' => $action,
            ],
        ];

        curl_setopt($ch, \CURLOPT_POSTFIELDS, json_encode($data, \JSON_UNESCAPED_UNICODE));
        curl_setopt($ch, \CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'charset=utf-8',
        ]);
        curl_setopt($ch, \CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        curl_close($ch);

        $arrResponse = json_decode((string) $response, true);

        if (isset($arrResponse['error'])) {
            $errorMessage = (string) ($arrResponse['error']['message'] ?? 'reCAPTCHA error');

            return false;
        }

        return
            ($arrResponse['tokenProperties']['valid'] ?? false) === true
            && ($arrResponse['tokenProperties']['action'] ?? null) === $action
            && ($arrResponse['riskAnalysis']['score'] ?? 0) >= 0.9;
    }

    private function validateV3($request, string $token, ?string &$errorMessage = null): bool
    {
        $errorMessage = null;

        $ch = curl_init();
        curl_setopt($ch, \CURLOPT_URL, 'https://www.google.com/recaptcha/api/siteverify');
        curl_setopt($ch, \CURLOPT_POST, 1);
        curl_setopt(
            $ch,
            \CURLOPT_POSTFIELDS,
            http_build_query([
                'secret' => $this->reCaptchaKeyPrivate,
                'response' => $token,
                'remoteip' => $request->getClientIp(),
            ])
        );
        curl_setopt($ch, \CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        curl_close($ch);

        $arrResponse = json_decode((string) $response, true);

        return ($arrResponse['success'] ?? null) === '1' && ($arrResponse['score'] ?? 0) >= 0.9;
    }
}
