<?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\Tests\Altcha;

use CpCreation\AltchaBundle\CpCreationAltchaBundle;
use CpCreation\AltchaBundle\Form\AltchaType;
use CpCreation\AltchaBundle\Scope\ScopeRegistry;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Altcha\VitiCoreAltchaProtection;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Altcha\VitiCoreAltchaScopeProvider;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\ContactFormType;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\PaymentType;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\ProductReviewReportType;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\Security\RegisterConfirmType;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\Security\ResetPasswordType;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\Withdrawal\WithdrawalRequestType;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Service\FormThemeLocator;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\FormThemeExtension;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Bundle\TwigBundle\TwigBundle;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
use Twig\Environment;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;

final class VitiCoreAltchaKernelIntegrationTest extends TestCase
{
    private ?VitiCoreAltchaTestKernel $kernel = null;

    protected function tearDown(): void
    {
        $this->kernel?->shutdown();
    }

    public function testKernelAutoconfigurationCollectsTheProviderAndBuildsEveryProtectedForm(): void
    {
        $this->kernel = new VitiCoreAltchaTestKernel('test', true);
        $this->kernel->boot();
        $container = $this->kernel->getContainer()->get('test.service_container');

        $requestStack = $container->get('request_stack');
        self::assertInstanceOf(RequestStack::class, $requestStack);
        $requestStack->push(Request::create('https://example.test/form'));

        $registry = $container->get('test.altcha.scope_registry');
        self::assertInstanceOf(ScopeRegistry::class, $registry);
        self::assertSame(
            [
                'marketing.product_review_report',
                'payment.online',
                'security.password_reset',
                'security.registration_confirm',
                'shop.withdrawal',
                'site.contact',
            ],
            array_map(static fn ($definition): string => $definition->name, $registry->manifest()->scopes),
        );

        $factory = $container->get('test.altcha.form_factory');
        self::assertInstanceOf(FormFactoryInterface::class, $factory);
        foreach ([
            ContactFormType::class => VitiCoreAltchaProtection::CONTACT,
            PaymentType::class => VitiCoreAltchaProtection::PAYMENT,
            RegisterConfirmType::class => VitiCoreAltchaProtection::REGISTRATION_CONFIRM,
            ResetPasswordType::class => VitiCoreAltchaProtection::PASSWORD_RESET,
            ProductReviewReportType::class => VitiCoreAltchaProtection::PRODUCT_REVIEW_REPORT,
            WithdrawalRequestType::class => VitiCoreAltchaProtection::WITHDRAWAL,
        ] as $formType => $scope) {
            $form = $factory->create($formType);

            self::assertTrue($form->has('captcha'));
            $captchaConfig = $form->get('captcha')->getConfig();
            self::assertInstanceOf(
                AltchaType::class,
                $captchaConfig->getType()->getInnerType(),
            );
            self::assertFalse($captchaConfig->getOption('subject_limits_enabled'));
            self::assertNull($captchaConfig->getOption('subject_path'));
            self::assertSame(
                VitiCoreAltchaProtection::partialLiveActionsFor($formType),
                $captchaConfig->getOption('partial_live_actions'),
            );
            self::assertSame(
                VitiCoreAltchaProtection::finalLiveActionsFor($formType),
                $captchaConfig->getOption('final_live_actions'),
            );

            $captchaView = $form->createView()->children['captcha'];
            self::assertStringContainsString('cpcreation-altcha', (string) $captchaView->vars['attr']['data-controller']);
            self::assertStringContainsString(rawurlencode($scope), (string) $captchaView->vars['altcha_challenge_url']);
        }
    }

    public function testContactComponentRendersAltchaWithTheIsolatedFrontFormTheme(): void
    {
        $this->kernel = new VitiCoreAltchaTestKernel('test', true);
        $this->kernel->boot();
        $container = $this->kernel->getContainer()->get('test.service_container');

        $requestStack = $container->get('request_stack');
        self::assertInstanceOf(RequestStack::class, $requestStack);
        $requestStack->push(Request::create('https://example.test/contact'));

        $factory = $container->get('test.altcha.form_factory');
        self::assertInstanceOf(FormFactoryInterface::class, $factory);
        $formView = $factory->create(ContactFormType::class)->createView();
        self::assertSame(
            ['form', 'cp_creation_altcha', '_contact_form_captcha'],
            $formView->children['captcha']->vars['block_prefixes'],
        );

        $twig = $container->get('test.altcha.twig');
        self::assertInstanceOf(Environment::class, $twig);
        $formThemeLocator = $container->get('test.altcha.form_theme_locator');
        self::assertInstanceOf(FormThemeLocator::class, $formThemeLocator);
        self::assertSame([
            '@CpCreationAltcha/form/altcha_theme.html.twig',
            'extends/form/form.html.twig',
        ], $formThemeLocator->scanThemes());
        $html = $twig->render('components/site/ContactForm.html.twig', [
            'attributes' => '',
            'form' => $formView,
        ]);

        $crawler = new Crawler($html);
        self::assertCount(1, $crawler->filter('.cpcreation-altcha-row'));
        self::assertCount(1, $crawler->filter('altcha-widget'));
        $opaqueHoneypot = $crawler->filter('.cpcreation-altcha-honeypot input');
        self::assertCount(1, $opaqueHoneypot);
        self::assertSame('honeypot', $opaqueHoneypot->attr('data-cpcreation-altcha-target'));
        $honeypotMirror = $crawler->filter('#contact_form_captcha__honeypot');
        self::assertCount(1, $honeypotMirror);
        self::assertSame('uninitialized', $honeypotMirror->attr('value'));
        self::assertSame(
            'contact_form_captcha__honeypot',
            $crawler->filter('#contact_form_captcha')->attr('data-cpcreation-altcha-honeypot-input-id-value'),
        );
    }
}

final class VitiCoreAltchaTestKernel extends Kernel
{
    use MicroKernelTrait;

    public function registerBundles(): iterable
    {
        yield new FrameworkBundle();
        yield new TwigBundle();
        yield new CpCreationAltchaBundle();
    }

    public function getCacheDir(): string
    {
        return sys_get_temp_dir().'/viticore_altcha_bundle/cache-with-host-theme';
    }

    public function getProjectDir(): string
    {
        return __DIR__.'/Fixtures/host-project';
    }

    public function getLogDir(): string
    {
        return sys_get_temp_dir().'/viticore_altcha_bundle/log';
    }

    private function configureContainer(ContainerConfigurator $container): void
    {
        $container->extension('framework', [
            'secret' => 'kernel-test-secret',
            'test' => true,
            'http_method_override' => false,
            'router' => ['utf8' => true],
            'http_client' => ['enabled' => true],
            'validation' => ['enabled' => true],
            'form' => ['enabled' => true],
            'translator' => ['enabled' => true, 'fallbacks' => ['fr']],
        ]);
        $container->extension('cp_creation_altcha', [
            'service_url' => 'http://127.0.0.1:1',
            'runtime_token' => 'runtime-test-token',
            'scope_sync_token' => 'scope-sync-test-token',
            'envelope_secret' => 'kernel-envelope-test-secret-at-least-32-bytes',
            'subject_secret' => '',
        ]);
        $container->extension('twig', [
            'strict_variables' => true,
            'paths' => [\dirname(__DIR__, 2).'/src/Bridge/Symfony/Bundle/Resources/views'],
        ]);

        $services = $container->services();
        $services->set(VitiCoreFrontFormThemeTestExtension::class)
            ->autoconfigure();
        $services->set(FormThemeLocator::class)
            ->autowire();
        $services->set(FormThemeExtension::class)
            ->autowire()
            ->autoconfigure();
        $services->set(VitiCoreAltchaScopeProvider::class)
            ->autowire()
            ->autoconfigure();
        foreach ([
            ContactFormType::class,
            RegisterConfirmType::class,
            ResetPasswordType::class,
            ProductReviewReportType::class,
            WithdrawalRequestType::class,
        ] as $formType) {
            $services->set($formType)
                ->autowire()
                ->autoconfigure();
        }
        $services->alias('test.altcha.scope_registry', ScopeRegistry::class)->public();
        $services->alias('test.altcha.form_factory', 'form.factory')->public();
        $services->alias('test.altcha.twig', 'twig')->public();
        $services->alias('test.altcha.form_theme_locator', FormThemeLocator::class)->public();
    }

    private function configureRoutes(RoutingConfigurator $routes): void
    {
        $bundleFile = (new \ReflectionClass(CpCreationAltchaBundle::class))->getFileName();
        if (false === $bundleFile) {
            throw new \LogicException('Unable to locate the installed ALTCHA bundle.');
        }

        $routes->import(\dirname($bundleFile).'/Resources/config/routes.php');
    }
}

final class VitiCoreFrontFormThemeTestExtension extends AbstractExtension
{
    public function getFilters(): array
    {
        return [
            new TwigFilter('imagine_filter', static fn (string $path): string => $path),
        ];
    }
}
