<?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\Form\Site;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Notification\Command\NotificationCommand;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Notification\Enum\PushLevel;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Translation\TranslatorInterface;

final class RecaptchaType extends AbstractType
{
    public function __construct(
        private readonly string $reCaptchaKeyPrivate,
        private readonly string $reCaptchaKeyPublic,
        private readonly bool $reCaptchaEnterprise,
        private readonly string $reCaptchaProject,
        private readonly RequestStack $requestStack,
        private readonly TranslatorInterface $translator,
        private readonly MessageBusInterface $commandSimpleBus,
    ) {
    }

    public function buildView(FormView $view, FormInterface $form, array $options): void
    {
        $attr = $view->vars['attr'] ?? [];

        $attr['class'] = $this->reCaptchaEnterprise ? 'g_recaptcha_enterprise' : 'g_recaptcha';
        $attr['data-controller'] = 'recaptcha';
        $attr['data-sitekey'] = $this->reCaptchaKeyPublic;

        $view->vars['attr'] = $attr;
    }

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event): void {
            $request = $this->requestStack->getMainRequest();
            if (!$request) {
                return;
            }

            // même logique que toi : valider uniquement sur turbo_stream
            if ('turbo_stream' !== $request->getPreferredFormat()) {
                return;
            }

            $form = $event->getForm();
            $token = (string) $form->getData();

            if ('' === $token) {
                $this->addRecaptchaError($form, null);

                return;
            }

            $action = $form->getParent()
                ? $form->getParent()->getName().'_g_recaptcha_response'
                : 'g_recaptcha_response';

            $ok = $this->reCaptchaEnterprise
                ? $this->verifyEnterprise($token, $action, $request->headers->get('User-Agent'), $request->getClientIp())
                : $this->verifyClassic($token, $request->getClientIp());

            if ($ok) {
                return;
            }

            $this->addRecaptchaError($form, null);
        });
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'mapped' => false,
            'required' => false,
        ]);
    }

    public function getParent(): string
    {
        return HiddenType::class;
    }

    private function addRecaptchaError(FormInterface $form, ?string $details): void
    {
        $message = $details ?: $this->translator->trans('form.error-recaptcha', [], 'validators');
        $this->commandSimpleBus->dispatch(new NotificationCommand(
            message: $message,
            level: PushLevel::ERROR,
        ));

        $form->addError(new FormError($message));
    }

    private function verifyEnterprise(string $token, string $action, ?string $userAgent, ?string $ip): bool
    {
        $ch = curl_init();

        curl_setopt($ch, \CURLOPT_URL, \sprintf(
            'https://recaptchaenterprise.googleapis.com/v1/projects/%s/assessments?key=%s',
            $this->reCaptchaProject,
            $this->reCaptchaKeyPrivate
        ));
        curl_setopt($ch, \CURLOPT_POST, 1);

        $payload = [
            'event' => [
                'token' => $token,
                'siteKey' => $this->reCaptchaKeyPublic,
                'userAgent' => $userAgent,
                'userIpAddress' => $ip,
                'expectedAction' => $action,
            ],
        ];

        $dataJson = json_encode($payload, \JSON_UNESCAPED_UNICODE);
        curl_setopt($ch, \CURLOPT_POSTFIELDS, $dataJson);
        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);

        if (!\is_string($response) || '' === $response) {
            return false;
        }

        $arr = json_decode($response, true);
        if (!\is_array($arr)) {
            return false;
        }

        // erreurs API
        if (isset($arr['error'])) {
            return false;
        }

        $valid = (bool) ($arr['tokenProperties']['valid'] ?? false);
        $score = (float) ($arr['riskAnalysis']['score'] ?? 0.0);

        return $valid && $score >= 0.8;
    }

    private function verifyClassic(string $token, ?string $ip): bool
    {
        $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' => $ip,
        ]));
        curl_setopt($ch, \CURLOPT_RETURNTRANSFER, true);

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

        if (!\is_string($response) || '' === $response) {
            return false;
        }

        $arr = json_decode($response, true);
        if (!\is_array($arr)) {
            return false;
        }

        $success = (bool) ($arr['success'] ?? false);
        $score = (float) ($arr['score'] ?? 0.0);

        return $success && $score >= 0.8;
    }
}
