<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CmsBundle\Tests\Form;

use Ferienpass\CmsBundle\Form\ChangePasswordFormType;
use Ferienpass\CmsBundle\Form\CompoundType\PostalAddressType;
use Ferienpass\CmsBundle\Form\RegistrationWelcomeType;
use Ferienpass\CmsBundle\Form\ResetPasswordRequestFormType;
use Ferienpass\CmsBundle\Form\UserChangePasswordType;
use Ferienpass\CmsBundle\Form\UserLoginType;
use Ferienpass\CmsBundle\Form\UserParticipantsType;
use Ferienpass\CmsBundle\Form\UserPersonalDataType;
use Ferienpass\CmsBundle\Form\UserRegistrationType;
use Ferienpass\CoreBundle\Entity\User;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Regex;
use Symfony\Component\Validator\Validation;

final class CmsFormTypesTest extends TestCase
{
    // ---- ResetPasswordRequestFormType ----

    public function testResetPasswordRequestFormTypeOptions(): void
    {
        $resolver = new OptionsResolver();
        new ResetPasswordRequestFormType()->configureOptions($resolver);
        $resolved = $resolver->resolve([]);

        self::assertSame('user.%name%', $resolved['label_format']);
        self::assertSame('cms', $resolved['translation_domain']);
    }

    // ---- ChangePasswordFormType ----

    public function testChangePasswordFormTypeOptions(): void
    {
        $resolver = new OptionsResolver();
        new ChangePasswordFormType()->configureOptions($resolver);
        $resolved = $resolver->resolve([]);

        self::assertSame('passwordReset.%name%', $resolved['label_format']);
        self::assertSame('cms', $resolved['translation_domain']);
        self::assertFalse($resolved['csrf_protection']);
    }

    // ---- UserLoginType ----

    public function testUserLoginTypeOptions(): void
    {
        $resolver = new OptionsResolver();
        new UserLoginType()->configureOptions($resolver);
        $resolved = $resolver->resolve([]);

        self::assertFalse($resolved['mapped']);
        self::assertSame('login.%name%', $resolved['label_format']);
        self::assertSame('cms', $resolved['translation_domain']);
        self::assertFalse($resolved['csrf_protection']);
    }

    // ---- UserRegistrationType ----

    public function testUserRegistrationTypeDefaultOptions(): void
    {
        $resolver = new OptionsResolver();
        new UserRegistrationType()->configureOptions($resolver);
        $resolved = $resolver->resolve([]);

        self::assertFalse($resolved['mapped']);
        self::assertSame('registration.%name%', $resolved['label_format']);
        self::assertSame('cms', $resolved['translation_domain']);
        self::assertTrue($resolved['ask_phone']);
        self::assertTrue($resolved['ask_mobile']);
        self::assertFalse($resolved['csrf_protection']);
    }

    public function testUserRegistrationTypeAskPhoneCanBeOverridden(): void
    {
        $resolver = new OptionsResolver();
        new UserRegistrationType()->configureOptions($resolver);
        $resolved = $resolver->resolve(['ask_phone' => false]);

        self::assertFalse($resolved['ask_phone']);
    }

    // ---- UserPersonalDataType ----

    public function testUserPersonalDataTypeOptions(): void
    {
        $resolver = new OptionsResolver();
        new UserPersonalDataType()->configureOptions($resolver);
        $resolved = $resolver->resolve([]);

        self::assertSame(User::class, $resolved['data_class']);
        self::assertFalse($resolved['required']);
        self::assertSame('user.%name%', $resolved['label_format']);
        self::assertSame('cms', $resolved['translation_domain']);
        self::assertFalse($resolved['csrf_protection']);
    }

    // ---- RegistrationWelcomeType ----

    public function testRegistrationWelcomeTypeOptions(): void
    {
        $resolver = new OptionsResolver();
        new RegistrationWelcomeType()->configureOptions($resolver);
        $resolved = $resolver->resolve([]);

        self::assertSame(User::class, $resolved['data_class']);
        self::assertFalse($resolved['required']);
        self::assertSame('user.%name%', $resolved['label_format']);
        self::assertSame('cms', $resolved['translation_domain']);
    }

    // ---- UserChangePasswordType ----

    public function testUserChangePasswordTypeOptions(): void
    {
        $resolver = new OptionsResolver();
        new UserChangePasswordType()->configureOptions($resolver);
        $resolved = $resolver->resolve([]);

        self::assertSame('changePassword.%name%', $resolved['label_format']);
        self::assertSame('cms', $resolved['translation_domain']);
    }

    // ---- UserParticipantsType ----

    public function testUserParticipantsTypeOptions(): void
    {
        $resolver = new OptionsResolver();
        new UserParticipantsType()->configureOptions($resolver);
        $resolved = $resolver->resolve([]);

        self::assertSame('user.%name%', $resolved['label_format']);
        self::assertSame('cms', $resolved['translation_domain']);
    }

    // ---- PostalAddressType: street must include a house number ----

    public function testPostalAddressTypeStreetRequiresHouseNumber(): void
    {
        $captured = [];
        $builder = $this->createMock(FormBuilderInterface::class);
        $builder->method('add')->willReturnCallback(
            static function (string $name, ?string $type = null, array $options = []) use (&$captured, $builder): FormBuilderInterface {
                $captured[$name] = $options;

                return $builder;
            },
        );

        new PostalAddressType()->buildForm($builder, []);

        self::assertArrayHasKey('street', $captured);

        $regex = null;
        foreach ($captured['street']['constraints'] ?? [] as $constraint) {
            if ($constraint instanceof Regex) {
                $regex = $constraint;
                break;
            }
        }

        self::assertInstanceOf(Regex::class, $regex, 'The street field must carry a house-number Regex constraint.');

        // A street name alone is rejected; with a number it passes; empty is
        // skipped (Regex validates only non-empty values).
        $validator = Validation::createValidator();
        self::assertCount(1, $validator->validate('Musterweg', $regex));
        self::assertCount(0, $validator->validate('Musterweg 12', $regex));
        self::assertCount(0, $validator->validate('', $regex));
    }
}
