<?php

declare(strict_types=1);

namespace NoahVet\Reef\Test\A_Unit\Domain\Validation;

use NoahVet\Reef\Domain\Validation\DateValidator;
use NoahVet\Reef\Domain\Validation\Enum\SupportedFormatEnum;
use NoahVet\Reef\Domain\Validation\Enum\SupportedTypeEnum;
use NoahVet\Reef\Domain\Validation\OpenApiValidator;
use NoahVet\Reef\Domain\Validation\OpenApiValidatorInterface;
use PHPUnit\Framework\TestCase;

final class OpenApiValidatorTest extends TestCase
{
    private OpenApiValidatorInterface $validator;

    protected function setUp(): void
    {
        $this->validator = new OpenApiValidator(
            new DateValidator(),
        );
    }

    /**
     * When all fields are correctly filled in, it must not return an error.
     * If the field is missing, then an error must be returned.
     */
    public function testAllNestedRequiredStringKeyShouldBeFilled(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'badParent',
                    'value' => null,
                    'message' => 'required',
                ],
                [
                    'key' => 'badParent',
                    'value' => null,
                    'message' => 'type.object',
                ],
            ],
            $this->validator->validate(
                [
                    'parent' => [
                        'child' => 'loulou',
                    ],
                    'badParent' => null,
                    'badParent2' => [
                        'child' => null,
                    ],
                ],
                [
                    'parent' => [
                        'type' => 'object',
                        'properties' => [
                            'child' => [
                                'type' => 'string',
                                'nullable' => false,
                            ],
                        ],
                    ],
                    'badParent' => [
                        'type' => 'object',
                        'properties' => [
                            'child' => [
                                'type' => 'string',
                                'nullable' => false,
                            ],
                        ],
                    ],
                ],
            ),
        );
    }

    /**
     * When all fields are correctly filled in, it must not return an error.
     * If the field is missing, then an error must be returned.
     */
    public function testAllRequiredStringKeyShouldBeFilled(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'missing',
                    'value' => null,
                    'message' => 'required',
                ],
            ],
            $this->validator->validate(
                [
                    'notMissing' => 'riri',
                ],
                [
                    'notMissing' => [
                        'type' => 'string',
                        'nullable' => false,
                    ],
                    'missing' => [
                        'type' => 'string',
                        'nullable' => false,
                    ],
                    'missingButNullable' => [
                        'type' => 'string',
                        'nullable' => true,
                    ],
                ],
            ),
        );
    }

    /**
     * When an enum field is correctly filled in, it must not return an error.
     * If the field is not in the enum list, then an error must be returned.
     */
    public function testArrayEnum(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'pokemonType2',
                    'value' => 'PIKACHU',
                    'message' => 'type.array',
                ],
                [
                    'key' => 'pokemonType4',
                    'value' => ['reptilo'],
                    'message' => 'enum.value',
                ],
                [
                    'key' => 'pokemonType5',
                    'value' => ['reptilo', 'bouleau'],
                    'message' => 'enum.value',
                ],
                [
                    'key' => 'pokemonType7',
                    'value' => [],
                    'message' => 'type.array.minItems',
                ],
            ],
            $this->validator->validate(
                [
                    'pokemonType' => ['PIKACHU'],
                    'pokemonType2' => 'PIKACHU',
                    'pokemonType3' => null,
                    'pokemonType4' => ['reptilo'],
                    'pokemonType5' => ['reptilo', 'bouleau'],
                    'pokemonType6' => [],
                    'pokemonType7' => [],
                    'pokemonType8' => [],
                    'pokemonType9' => [
                        'names' => [],
                    ],
                ],
                [
                    'pokemonType' => [
                        'type' => 'array',
                        'items' => [
                            'enum' => [
                                'PIKACHU',
                            ],
                            'type' => 'string',
                        ],
                    ],
                    'pokemonType2' => [
                        'type' => 'array',
                        'nullable' => false,
                        'items' => [
                            'enum' => [
                                'PIKACHU',
                            ],
                            'type' => 'string',
                        ],
                    ],
                    'pokemonType3' => [
                        'type' => 'array',
                        'nullable' => true,
                        'items' => [
                            'enum' => [
                                'PIKACHU',
                            ],
                            'type' => 'string',
                        ],
                    ],
                    'pokemonType4' => [
                        'type' => 'array',
                        'items' => [
                            'enum' => [
                                'PIKACHU',
                            ],
                            'type' => 'string',
                        ],
                    ],
                    'pokemonType5' => [
                        'type' => 'array',
                        'items' => [
                            'enum' => [
                                'PIKACHU',
                            ],
                            'type' => 'string',
                        ],
                    ],
                    'pokemonType6' => [
                        'type' => 'array',
                        'minItems' => 0,
                        'items' => [
                            'enum' => [
                                'PIKACHU',
                            ],
                            'type' => 'string',
                        ],
                    ],
                    'pokemonType7' => [
                        'type' => 'array',
                        'minItems' => 1,
                        'items' => [
                            'enum' => [
                                'PIKACHU',
                            ],
                            'type' => 'string',
                        ],
                    ],
                    'pokemonType8' => [
                        'type' => 'array',
                        'items' => [
                            'enum' => [
                                'PIKACHU',
                            ],
                            'type' => 'string',
                        ],
                    ],
                    'pokemonType9' => [
                        'properties' => [
                            'names' => [
                                'type' => 'array',
                                'items' => [
                                    'properties' => [
                                        'name' => [
                                            'type' => 'string',
                                        ],
                                    ],
                                    'type' => 'object',
                                ],
                            ],
                        ],
                        'type' => 'object',
                    ],
                ],
            ),
        );
    }

    /**
     * When an array field is correctly filled in, it must not return an error.
     * If the field is of another type, then an error must be returned.
     */
    public function testArrayType(): void
    {
        $this->assertSame(
            [],
            $this->validator->validate(
                [
                    'idCollection' => [
                        0 => [
                            'id' => '660c1473-ad37-4aae-afd4-1ff486d7f6f2',
                        ],
                        1 => [
                            'id' => '660c1473-ad37-4aae-afd4-1ff486d7f6f3',
                        ],
                    ],
                ],
                [
                    'idCollection' => [
                        'type' => 'array',
                        'items' => [
                            'properties' => [
                                'id' => [
                                    'type' => 'string',
                                    'format' => 'uuid',
                                ],
                            ],
                            'type' => 'object',
                        ],
                    ],
                ],
            ),
        );
    }

    /**
     * When an boolean field is correctly filled in, it must not return an error.
     * If the field is of another type, then an error must be returned.
     */
    public function testBooleanType(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'example1',
                    'value' => "I\'am a string.",
                    'message' => 'type.boolean',
                ],
                [
                    'key' => 'example2',
                    'value' => 0,
                    'message' => 'type.boolean',
                ],
            ],
            $this->validator->validate(
                $this->getSimpleScalarExample(),
                [
                    'example1' => [
                        'type' => 'boolean',
                    ],
                    'example2' => [
                        'type' => 'boolean',
                    ],
                    'example3' => [
                        'type' => 'boolean',
                    ],
                ],
            ),
        );
    }

    /**
     * A nullable datetime could be filled.
     */
    public function testDatetimeWithNullValue(): void
    {
        $this->assertSame(
            [],
            $this->validator->validate(
                [
                    'leavingDate' => null,
                ],
                [
                    'leavingDate' => [
                        'format' => 'date-time',
                        'nullable' => true,
                        'type' => 'string',
                    ],
                ],
            ),
        );
    }

    /**
     * A date with a datetime value is not correct.
     */
    public function testDateWithDatetimeValue(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'leavingDate',
                    'value' => '2022-03-28T00:00:00+00:00',
                    'message' => 'format.date',
                ],
            ],
            $this->validator->validate(
                [
                    'leavingDate' => '2022-03-28T00:00:00+00:00',
                ],
                [
                    'leavingDate' => [
                        'format' => 'date',
                        'type' => 'string',
                    ],
                ],
            ),
        );
    }

    /**
     * A date with a correct date value.
     */
    public function testDateWithGoodDateValue(): void
    {
        $this->assertSame(
            [],
            $this->validator->validate(
                [
                    'leavingDate' => '2022-03-28',
                ],
                [
                    'leavingDate' => [
                        'format' => 'date',
                        'type' => 'string',
                    ],
                ],
            ),
        );
    }

    /**
     * A nullable date could be filled.
     */
    public function testDateWithNullValue(): void
    {
        $this->assertSame(
            [],
            $this->validator->validate(
                [
                    'leavingDate' => null,
                ],
                [
                    'leavingDate' => [
                        'format' => 'date',
                        'nullable' => true,
                        'type' => 'string',
                    ],
                ],
            ),
        );
    }

    /**
     * The behavior of a nested object must be the same as an object.
     *
     * @see OpenApiValidatorTest::testObjectType()
     */
    public function testDeepNestedObjectType(): void
    {
        $this->assertSame(
            [],
            $this->validator->validate(
                [
                    'dependency' => [
                        'nestedDependency' => [
                            'deepNestedDependency' => [
                                'id' => '660c1473-ad37-4aae-afd4-1ff486d7f6f2',
                            ],
                        ],
                    ],
                ],
                [
                    'dependency' => [
                        'type' => 'object',
                        'properties' => [
                            'nestedDependency' => [
                                'type' => 'object',
                                'properties' => [
                                    'deepNestedDependency' => [
                                        'type' => 'object',
                                        'properties' => [
                                            'id' => [
                                                'type' => 'string',
                                                'format' => 'uuid',
                                            ],
                                        ],
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ),
        );
    }

    /**
     * When an email string field is correctly filled in, it must not return an error.
     * If the field is of another type, then an error must be returned.
     */
    public function testEmailFormat(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'badEmail',
                    'value' => null,
                    'message' => 'required',
                ],
                [
                    'key' => 'badEmail2',
                    'value' => 'fifi',
                    'message' => 'format.email',
                ],
                [
                    'key' => 'badEmail3',
                    'value' => '@lol.fr',
                    'message' => 'format.email',
                ],
                [
                    'key' => 'badEmail4',
                    'value' => '4.fr',
                    'message' => 'format.email',
                ],
            ],
            $this->validator->validate(
                [
                    'goodEmail' => 'riri@gmail.com',
                    'badEmail' => null,
                    'badEmail2' => 'fifi',
                    'badEmail3' => '@lol.fr',
                    'badEmail4' => '4.fr',
                ],
                [
                    'goodEmail' => [
                        'type' => 'string',
                        'format' => 'email',
                    ],
                    'badEmail' => [
                        'type' => 'string',
                        'format' => 'email',
                    ],
                    'badEmail2' => [
                        'type' => 'string',
                        'format' => 'email',
                    ],
                    'badEmail3' => [
                        'type' => 'string',
                        'format' => 'email',
                    ],
                    'badEmail4' => [
                        'type' => 'string',
                        'format' => 'email',
                    ],
                ],
            ),
        );
    }

    /**
     * If there is no data to submit, the validator must not return an error.
     */
    public function testEmpty(): void
    {
        $this->assertSame(
            [],
            $this->validator->validate(
                [],
                [],
            ),
        );
    }

    /**
     * When an enum field is correctly filled in, it must not return an error.
     * If the field is not in the enum list, then an error must be returned.
     */
    public function testEnum(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'pokemonType4',
                    'value' => 'riri',
                    'message' => 'enum.value',
                ],
            ],
            $this->validator->validate(
                [
                    'pokemonType' => 'PIKACHU',
                    'pokemonType2' => 'PIKACHU',
                    'pokemonType3' => null,
                    'pokemonType4' => 'riri',
                ],
                [
                    'pokemonType' => [
                        'enum' => [
                            'PIKACHU',
                        ],
                        'type' => 'string',
                    ],
                    'pokemonType2' => [
                        'enum' => [
                            'PIKACHU',
                        ],
                        'nullable' => false,
                        'type' => 'string',
                    ],
                    'pokemonType3' => [
                        'enum' => [
                            'PIKACHU',
                        ],
                        'nullable' => true,
                        'type' => 'string',
                    ],
                    'pokemonType4' => [
                        'enum' => [
                            'PIKACHU',
                        ],
                        'type' => 'string',
                    ],
                ],
            ),
        );
    }

    /**
     * When an int array field is correctly filled in, it must not return an error.
     * If the field is of another type, then an error must be returned.
     */
    public function testIntArrayType(): void
    {
        $this->assertSame(
            [],
            $this->validator->validate(
                [
                    'vat' => [
                        5,
                        11,
                        20,
                    ],
                ],
                [
                    'vat' => [
                        'type' => 'array',
                        'items' => [
                            'type' => 'integer',
                        ],
                    ],
                ],
            ),
        );
    }

    /**
     * When an integer field is correctly filled in, it must not return an error.
     * If the field is of another type, then an error must be returned.
     */
    public function testIntegerType(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'example1',
                    'value' => "I\'am a string.",
                    'message' => 'type.integer',
                ],
                [
                    'key' => 'example3',
                    'value' => true,
                    'message' => 'type.integer',
                ],
            ],
            $this->validator->validate(
                $this->getSimpleScalarExample(),
                [
                    'example1' => [
                        'type' => 'integer',
                    ],
                    'example2' => [
                        'type' => 'integer',
                    ],
                    'example3' => [
                        'type' => 'integer',
                    ],
                ],
            ),
        );
    }

    /**
     * When integer field is correctly filled with maximum > value < minimum  value, it must not return an error.
     * Else an error must be returned.
     */
    public function testMinimumMaximumFormat(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'badPositiveValue',
                    'value' => 2,
                    'message' => 'integer.minimum',
                    'template' => 'The value of `badPositiveValue` key must be greater than 3.',
                ],
                [
                    'key' => 'badPositiveValue2',
                    'value' => 6,
                    'message' => 'integer.maximum',
                    'template' => 'The value of `badPositiveValue2` key must be lower than 5.',
                ],
                [
                    'key' => 'badMinimumNegativeValue',
                    'value' => -20,
                    'message' => 'integer.minimum',
                    'template' => 'The value of `badMinimumNegativeValue` key must be greater than -10.',
                ],
                [
                    'key' => 'badMaximumNegativeValue',
                    'value' => -5,
                    'message' => 'integer.maximum',
                    'template' => 'The value of `badMaximumNegativeValue` key must be lower than -10.',
                ],
                [
                    'key' => 'badMinimumNegativeNumberValue',
                    'value' => -16.666,
                    'message' => 'number.minimum',
                    'template' => 'The value of `badMinimumNegativeNumberValue` key must be greater than -15.',
                ],
                [
                    'key' => 'badMaximumNegativeNumberValue',
                    'value' => -9.999,
                    'message' => 'number.maximum',
                    'template' => 'The value of `badMaximumNegativeNumberValue` key must be lower than -10.',
                ],
            ],
            $this->validator->validate(
                [
                    'positiveValue' => 4,
                    'badPositiveValue' => 2,
                    'badPositiveValue2' => 6,
                    'minimumNegativeValue' => -2,
                    'badMinimumNegativeValue' => -20,
                    'maximumNegativeValue' => -15,
                    'badMaximumNegativeValue' => -5,
                    'minimumNegativeNumberValue' => -15.678,
                    'maximumNegativeNumberValue' => -15.677,
                    'badMinimumNegativeNumberValue' => -16.666,
                    'badMaximumNegativeNumberValue' => -9.999,
                ],
                [
                    'positiveValue' => [
                        'type' => 'integer',
                        'minimum' => 3,
                        'maximum' => 5,
                    ],
                    'badPositiveValue' => [
                        'type' => 'integer',
                        'minimum' => 3,
                        'maximum' => 5,
                    ],
                    'badPositiveValue2' => [
                        'type' => 'integer',
                        'minimum' => 3,
                        'maximum' => 5,
                    ],
                    'minimumNegativeValue' => [
                        'type' => 'integer',
                        'minimum' => -10,
                        'maximum' => 10,
                    ],
                    'badMinimumNegativeValue' => [
                        'type' => 'integer',
                        'minimum' => -10,
                        'maximum' => 10,
                    ],
                    'maximumNegativeValue' => [
                        'type' => 'integer',
                        'minimum' => -20,
                        'maximum' => -10,
                    ],
                    'badMaximumNegativeValue' => [
                        'type' => 'integer',
                        'minimum' => -20,
                        'maximum' => -10,
                    ],
                    'minimumNegativeNumberValue' => [
                        'type' => 'number',
                        'minimum' => -16,
                        'maximum' => -14,
                    ],
                    'maximumNegativeNumberValue' => [
                        'type' => 'number',
                        'minimum' => -16,
                        'maximum' => -14,
                    ],
                    'badMinimumNegativeNumberValue' => [
                        'type' => 'number',
                        'minimum' => -15,
                        'maximum' => -10,
                    ],
                    'badMaximumNegativeNumberValue' => [
                        'type' => 'number',
                        'minimum' => -13,
                        'maximum' => -10,
                    ],
                ],
            ),
        );
    }

    /**
     * When string field is correctly filled with minLength > value < maxLength  value, it must not return an error.
     * Else an error must be returned.
     */
    public function testMinMaxLengthFormat(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'tooSmallValue',
                    'value' => 'ab',
                    'message' => 'string.minLength',
                    'template' => 'The value of `tooSmallValue` key must be greater or equals to 3.',
                ],
                [
                    'key' => 'tooBigValue',
                    'value' => 'abcdefghij',
                    'message' => 'string.maxLength',
                    'template' => 'The value of `tooBigValue` key must be lower or equals to 8.',
                ],
            ],
            $this->validator->validate(
                [
                    'goodValue' => 'abcdef',
                    'tooSmallValue' => 'ab',
                    'tooBigValue' => 'abcdefghij',
                    'equalsToMinLength' => 'abc',
                    'equalsToMaxLength' => 'abcdefgh',
                ],
                [
                    'goodValue' => [
                        'type' => 'string',
                        'minLength' => 3,
                        'maxLength' => 8,
                    ],
                    'tooSmallValue' => [
                        'type' => 'string',
                        'minLength' => 3,
                        'maxLength' => 8,
                    ],
                    'tooBigValue' => [
                        'type' => 'string',
                        'minLength' => 3,
                        'maxLength' => 8,
                    ],
                    'equalsToMinLength' => [
                        'type' => 'string',
                        'minLength' => 3,
                        'maxLength' => 8,
                    ],
                    'equalsToMaxLength' => [
                        'type' => 'string',
                        'minLength' => 3,
                        'maxLength' => 8,
                    ],
                ],
            ),
        );
    }

    /**
     * When string field is correctly filled and respect the pattern, it must not return an error.
     * Else an error must be returned.
     */
    public function testPatternFormat(): void
    {
        $ibanRegex =
            '/^AL\d{10}[0-9A-Z]{16}$|^AD\d{10}[0-9A-Z]{12}$|^AT\d{18}$|^BH\d{2}[A-Z]{4}[0-9A-Z]{14}$|^BE\d{14}$|^B
        A\d{18}$|^BG\d{2}[A-Z]{4}\d{6}[0-9A-Z]{8}$|^HR\d{19}$|^CY\d{10}[0-9A-Z]{16}$|^CZ\d{22}$|^DK\d{16}$|^FO\d{16}$|^
        GL\d{16}$|^DO\d{2}[0-9A-Z]{4}\d{20}$|^EE\d{18}$|^FI\d{16}$|^FR\d{12}[0-9A-Z]{11}\d{2}$|^GE\d{2}[A-Z]{2}\d{16}$|
        ^DE\d{20}$|^GI\d{2}[A-Z]{4}[0-9A-Z]{15}$|^GR\d{9}[0-9A-Z]{16}$|^HU\d{26}$|^IS\d{24}$|^IE\d{2}[A-Z]{4}\d{14}$|^I
        L\d{21}$|^IT\d{2}[A-Z]\d{10}[0-9A-Z]{12}$|^[A-Z]{2}\d{5}[0-9A-Z]{13}$|^KW\d{2}[A-Z]{4}22!$|^LV\d{2}[A-Z]{4}[0-9
        A-Z]{13}$|^LB\d{6}[0-9A-Z]{20}$|^LI\d{7}[0-9A-Z]{12}$|^LT\d{18}$|^LU\d{5}[0-9A-Z]{13}$|^MK\d{5}[0-9A-Z]{10}\d{2
        }$|^MT\d{2}[A-Z]{4}\d{5}[0-9A-Z]{18}$|^MR13\d{23}$|^MU\d{2}[A-Z]{4}\d{19}[A-Z]{3}$|^MC\d{12}[0-9A-Z]{11}\d{2}$|
        ^ME\d{20}$|^NL\d{2}[A-Z]{4}\d{10}$|^NO\d{13}$|^PL\d{10}[0-9A-Z]{,16}n$|^PT\d{23}$|^RO\d{2}[A-Z]{4}[0-9A-Z]{16}$
        |^SM\d{2}[A-Z]\d{10}[0-9A-Z]{12}$|^SA\d{4}[0-9A-Z]{18}$|^RS\d{20}$|^SK\d{22}$|^SI\d{17}$|^ES\d{22}$|^SE\d{22}$|
        ^CH\d{7}[0-9A-Z]{12}$|^TN59\d{20}$|^TR\d{7}[0-9A-Z]{17}$|^AE\d{21}$|^GB\d{2}[A-Z]{4}\d{14}$/';

        $bicSwiftRegex = '/^[A-Z]{6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3})?$/';

        $phoneNumberRegex = '/^\+[1-9]\d{1,14}$/';

        $this->assertSame(
            [
                [
                    'key' => 'badIban',
                    'value' => 'BADPREFIX7630001007941234567890185',
                    'message' => 'string.pattern',
                    'template' => 'The value of `badIban` should be respect the pattern '.$ibanRegex.'.',
                ],
                [
                    'key' => 'badBicSwift',
                    'value' => 'qdsf11qs56df1562qs26d',
                    'message' => 'string.pattern',
                    'template' => 'The value of `badBicSwift` should be respect the pattern '.$bicSwiftRegex.'.',
                ],
                [
                    'key' => 'badPhoneNumber',
                    'value' => '0987878787878787',
                    'message' => 'string.pattern',
                    'template' => 'The value of `badPhoneNumber` should be respect the pattern '.$phoneNumberRegex.'.',
                ],
            ],
            $this->validator->validate(
                [
                    'goodIban' => 'FR7630001007941234567890185',
                    'goodBicSwift' => 'AGRIFRPP847',
                    'goodPhoneNumber' => '+33612345678',
                    'badIban' => 'BADPREFIX7630001007941234567890185',
                    'badBicSwift' => 'qdsf11qs56df1562qs26d',
                    'badPhoneNumber' => '0987878787878787',
                ],
                [
                    'goodIban' => [
                        'type' => 'string',
                        'pattern' => $ibanRegex,
                    ],
                    'goodBicSwift' => [
                        'type' => 'string',
                        'pattern' => $bicSwiftRegex,
                    ],
                    'goodPhoneNumber' => [
                        'type' => 'string',
                        'pattern' => $phoneNumberRegex,
                    ],
                    'badIban' => [
                        'type' => 'string',
                        'pattern' => $ibanRegex,
                    ],
                    'badBicSwift' => [
                        'type' => 'string',
                        'pattern' => $bicSwiftRegex,
                    ],
                    'badPhoneNumber' => [
                        'type' => 'string',
                        'pattern' => $phoneNumberRegex,
                    ],
                ],
            ),
        );
    }

    /**
     * When a number field is correctly filled in, it must not return an error.
     * If the field does not contain a value that is a multiple of, then an error must be returned.
     */
    public function testMultipleOf(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'example1',
                    'value' => 4,
                    'message' => 'multiple_of',
                    'template' => 'The value of `example1` key must be a multiple of 3.',
                ],
                [
                    'key' => 'example2',
                    'value' => 4,
                    'message' => 'multiple_of',
                    'template' => 'The value of `example2` key must be a multiple of 3.',
                ],
                [
                    'key' => 'example5',
                    'value' => '6',
                    'message' => 'type.integer',
                ],
                [
                    'key' => 'example6',
                    'value' => '6',
                    'message' => 'type.number',
                ],
            ],
            $this->validator->validate(
                [
                    'example1' => 4,
                    'example2' => 4,
                    'example3' => 6,
                    'example4' => 6,
                    'example5' => '6',
                    'example6' => '6',
                ],
                [
                    'example1' => [
                        'type' => 'integer',
                        'multipleOf' => 3,
                    ],
                    'example2' => [
                        'type' => 'number',
                        'multipleOf' => 3,
                    ],
                    'example3' => [
                        'type' => 'integer',
                        'multipleOf' => 3,
                    ],
                    'example4' => [
                        'type' => 'number',
                        'multipleOf' => 3,
                    ],
                    'example5' => [
                        'type' => 'integer',
                        'multipleOf' => 3,
                    ],
                    'example6' => [
                        'type' => 'number',
                        'multipleOf' => 3,
                    ],
                ],
            ),
        );
    }

    /**
     * The behavior of a nested object must be the same as an object.
     *
     * @see OpenApiValidatorTest::testObjectType()
     */
    public function testNestedObjectType(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'nullBadDependency',
                    'value' => null,
                    'message' => 'required',
                ],
                [
                    'key' => 'badDependency.badNestedDependency',
                    'value' => 'badNestedDependencyValue',
                    'message' => 'type.object',
                ],
                [
                    'key' => 'emptyBadDependency',
                    'value' => [],
                    'message' => 'type.object',
                ],
                [
                    'key' => 'nullBadDependency',
                    'value' => null,
                    'message' => 'type.object',
                ],
            ],
            $this->validator->validate(
                [
                    'dependency' => [
                        'nestedDependency' => [
                            'id' => '660c1473-ad37-4aae-afd4-1ff486d7f6f2',
                        ],
                    ],
                    'badDependency' => [
                        'badNestedDependency' => 'badNestedDependencyValue',
                    ],
                    'emptyBadDependency' => [],
                    'nullBadDependency' => null,
                    'nullableDependency' => null,
                ],
                [
                    'dependency' => [
                        'type' => 'object',
                        'properties' => [
                            'nestedDependency' => [
                                'type' => 'object',
                                'properties' => [
                                    'id' => [
                                        'type' => 'string',
                                        'format' => 'uuid',
                                    ],
                                ],
                            ],
                        ],
                    ],
                    'badDependency' => [
                        'type' => 'object',
                        'properties' => [
                            'badNestedDependency' => [
                                'type' => 'object',
                                'properties' => [
                                    'id' => [
                                        'type' => 'string',
                                        'format' => 'uuid',
                                    ],
                                ],
                            ],
                        ],
                    ],
                    'emptyBadDependency' => [
                        'type' => 'object',
                        'properties' => [
                            'badNestedDependency' => [
                                'type' => 'object',
                                'properties' => [
                                    'id' => [
                                        'type' => 'string',
                                        'format' => 'uuid',
                                    ],
                                ],
                            ],
                        ],
                    ],
                    'nullBadDependency' => [
                        'type' => 'object',
                        'properties' => [
                            'badNestedDependency' => [
                                'type' => 'object',
                                'properties' => [
                                    'id' => [
                                        'type' => 'string',
                                        'format' => 'uuid',
                                    ],
                                ],
                            ],
                        ],
                    ],
                    'nullableDependency' => [
                        'type' => 'object',
                        'nullable' => true,
                        'properties' => [
                            'badNestedDependency' => [
                                'type' => 'object',
                                'properties' => [
                                    'id' => [
                                        'type' => 'string',
                                        'format' => 'uuid',
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ),
        );
    }

    /**
     * When a nested object contain a key with value null, if the config is nullable true, it's work.
     */
    public function testNestedObjectWithNullValue(): void
    {
        $this->assertSame(
            [],
            $this->validator->validate(
                [
                    'schedule' => [
                        'monday' => null,
                    ],
                ],
                [
                    'schedule' => [
                        'properties' => [
                            'monday' => [
                                'items' => [
                                    'date' => [
                                        'type' => 'string',
                                    ],
                                    'type' => 'object',
                                ],
                                'nullable' => true,
                                'type' => 'array',
                            ],
                        ],
                        'type' => 'object',
                    ],
                ],
            ),
        );
    }

    /**
     * When a nullable integer field is correctly filled in, it must not return an error.
     * If the field is of another type, then an error must be returned.
     */
    public function testNullableIntegerType(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'exampleNotNullable2',
                    'value' => null,
                    'message' => 'required',
                ],
            ],
            $this->validator->validate(
                [
                    'exampleNullable' => 50,
                    'exampleNullable2' => null,
                    'exampleNotNullable' => 100,
                    'exampleNotNullable2' => null,
                ],
                [
                    'exampleNullable' => [
                        'type' => 'integer',
                        'nullable' => true,
                    ],
                    'exampleNullable2' => [
                        'type' => 'integer',
                        'nullable' => true,
                    ],
                    'exampleNotNullable' => [
                        'type' => 'integer',
                        'nullable' => false,
                    ],
                    'exampleNotNullable2' => [
                        'type' => 'integer',
                        'nullable' => false,
                    ],
                ],
            ),
        );
    }

    /**
     * When a nullable string field is correctly filled in, it must not return an error.
     * If the field is of another type, then an error must be returned.
     */
    public function testNullableStringType(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'exampleNotNullable2',
                    'value' => null,
                    'message' => 'required',
                ],
            ],
            $this->validator->validate(
                [
                    'exampleNullable' => 'riri',
                    'exampleNullable2' => null,
                    'exampleNotNullable' => 'fifi',
                    'exampleNotNullable2' => null,
                ],
                [
                    'exampleNullable' => [
                        'type' => 'string',
                        'nullable' => true,
                    ],
                    'exampleNullable2' => [
                        'type' => 'string',
                        'nullable' => true,
                    ],
                    'exampleNotNullable' => [
                        'type' => 'string',
                        'nullable' => false,
                    ],
                    'exampleNotNullable2' => [
                        'type' => 'string',
                        'nullable' => false,
                    ],
                ],
            ),
        );
    }

    /**
     * When a number array field is correctly filled in, it must not return an error.
     * If the field is of another type, then an error must be returned.
     */
    public function testNumberArrayType(): void
    {
        $this->assertSame(
            [],
            $this->validator->validate(
                [
                    'vat' => [
                        5.0,
                        -11,
                        20.2,
                    ],
                ],
                [
                    'vat' => [
                        'type' => 'array',
                        'items' => [
                            'type' => 'number',
                        ],
                    ],
                ],
            ),
        );
    }

    /**
     * When an object field is correctly filled in, it must not return an error.
     * If the field is of another type, then an error must be returned.
     */
    public function testObjectType(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'badDependency1',
                    'value' => 'bad',
                    'message' => 'type.object',
                ],
            ],
            $this->validator->validate(
                [
                    'dependency' => [
                        'id' => '660c1473-ad37-4aae-afd4-1ff486d7f6f2',
                    ],
                    'badDependency1' => 'bad',
                ],
                [
                    'dependency' => [
                        'type' => 'object',
                        'properties' => [
                            'id' => [
                                'type' => 'string',
                                'format' => 'uuid',
                            ],
                        ],
                    ],
                    'badDependency1' => [
                        'type' => 'object',
                        'properties' => [
                            'goodKey' => [
                                'type' => 'string',
                            ],
                        ],
                    ],
                ],
            ),
        );
    }

    /**
     * When a string array field is correctly filled in, it must not return an error.
     * If the field is of another type, then an error must be returned.
     */
    public function testStringArrayType(): void
    {
        $this->assertSame(
            [],
            $this->validator->validate(
                [
                    'departments' => [
                        'Marketing',
                        'Com',
                        'IT',
                    ],
                ],
                [
                    'departments' => [
                        'type' => 'array',
                        'items' => [
                            'type' => 'string',
                        ],
                    ],
                ],
            ),
        );
    }

    /**
     * When a string field is correctly filled in, it must not return an error.
     * If the field is of another type, then an error must be returned.
     */
    public function testStringType(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'example2',
                    'value' => 0,
                    'message' => 'type.string',
                ],
                [
                    'key' => 'example3',
                    'value' => true,
                    'message' => 'type.string',
                ],
            ],
            $this->validator->validate(
                $this->getSimpleScalarExample(),
                [
                    'example1' => [
                        'type' => 'string',
                    ],
                    'example2' => [
                        'type' => 'string',
                    ],
                    'example3' => [
                        'type' => 'string',
                    ],
                ],
            ),
        );
    }

    /**
     * If a format does not exist, then a logic exception must be thrown.
     */
    public function testThrowNewLogicExceptionWhenTheFormatIsNotSupported(): void
    {
        $this->expectException(\LogicException::class);
        $this->expectExceptionMessage(
            \sprintf(
                'The riri format doesn\'t exist. The supported formats are %s.',
                \json_encode(
                    SupportedFormatEnum::cases(),
                ),
            ),
        );

        $this->validator->validate(
            $this->getSimpleScalarExample(),
            [
                'example1' => [
                    'type' => 'string',
                    'format' => 'riri',
                ],
                'example2' => [
                    'type' => 'string',
                    'format' => 'fifi',
                ],
                'example3' => [
                    'type' => 'string',
                    'format' => 'loulou',
                ],
            ],
        );
    }

    /**
     * If a type key does not exist for array items, then a logic exception must be thrown.
     */
    public function testThrowNewLogicExceptionWhenTheArrayItemsTypeIsNotWritten(): void
    {
        $this->expectException(\LogicException::class);
        $this->expectExceptionMessage('The key type should be define for the items.');

        $this->validator->validate(
            [
                'example1' => [
                    'name' => 'example1_name1',
                ],
                'example2' => [
                    'name' => 'example2_name2',
                ],
            ],
            [
                'example1' => [
                    'type' => 'array',
                    'items' => [
                        'properties' => [
                            'name' => [
                                'type' => 'string',
                            ],
                        ],
                    ],
                ],
                'example2' => [
                    'type' => 'array',
                    'items' => [
                        'properties' => [
                            'name' => [
                                'type' => 'string',
                            ],
                        ],
                        'type' => 'object',
                    ],
                ],
            ],
        );
    }

    /**
     * If the maximum value is filled when the key type is not an integer, then a logic exception must be thrown.
     */
    public function testThrowNewLogicExceptionWhenTheMaximumIsFilledOnNotStringType(): void
    {
        $this->expectException(\LogicException::class);
        $this->expectExceptionMessage('The `maximum` key can only be applied to an integer / number type.');

        $this->validator->validate(
            [
                'value' => 4,
            ],
            [
                'value' => [
                    'type' => 'string',
                    'maximum' => 0,
                ],
            ],
        );
    }

    /**
     * If the maximum value is not an integer, then a logic exception must be thrown.
     */
    public function testThrowNewLogicExceptionWhenTheMaximumIsNotAnInteger(): void
    {
        $this->expectException(\LogicException::class);
        $this->expectExceptionMessage('The `maximum` value must be an integer.');

        $this->validator->validate(
            [
                'value' => 4,
            ],
            [
                'value' => [
                    'type' => 'integer',
                    'maximum' => 'fifi',
                ],
            ],
        );
    }

    /**
     * If the minimum value is filled when the key type is not an integer, then a logic exception must be thrown.
     */
    public function testThrowNewLogicExceptionWhenTheMinimumIsFilledOnNotStringType(): void
    {
        $this->expectException(\LogicException::class);
        $this->expectExceptionMessage('The `minimum` key can only be applied to an integer / number type.');

        $this->validator->validate(
            [
                'value' => 4,
            ],
            [
                'value' => [
                    'type' => 'string',
                    'minimum' => 0,
                ],
            ],
        );
    }

    /**
     * If the minimum value is not an integer, then a logic exception must be thrown.
     */
    public function testThrowNewLogicExceptionWhenTheMinimumIsNotAnInteger(): void
    {
        $this->expectException(\LogicException::class);
        $this->expectExceptionMessage('The `minimum` value must be an integer.');

        $this->validator->validate(
            [
                'value' => 4,
            ],
            [
                'value' => [
                    'type' => 'integer',
                    'minimum' => 'riri',
                ],
            ],
        );
    }

    /**
     * If the minimum value is not a number, then a logic exception must be thrown.
     */
    public function testThrowNewLogicExceptionWhenTheMinimumIsNotANumber(): void
    {
        $this->expectException(\LogicException::class);
        $this->expectExceptionMessage('The `minimum` value must be an integer.');

        $this->validator->validate(
            [
                'value' => 4.0,
            ],
            [
                'value' => [
                    'type' => 'number',
                    'minimum' => 'fifi',
                ],
            ],
        );
    }

    /**
     * If the maximum value is not a number, then a logic exception must be thrown.
     */
    public function testThrowNewLogicExceptionWhenTheMaximumIsNotANumber(): void
    {
        $this->expectException(\LogicException::class);
        $this->expectExceptionMessage('The `maximum` value must be an integer.');

        $this->validator->validate(
            [
                'value' => 4.0,
            ],
            [
                'value' => [
                    'type' => 'number',
                    'maximum' => 'fifi',
                ],
            ],
        );
    }

    /**
     * If the properties key is missing, then a logic exception must be thrown.
     */
    public function testThrowNewLogicExceptionWhenThePropertiesIsNotFilledForArrayType(): void
    {
        $this->expectException(\LogicException::class);
        $this->expectExceptionMessage('The key items should be define for the type array.');

        $this->validator->validate(
            [
                'dependencies' => [
                    [
                        'id' => '660c1473-ad37-4aae-afd4-1ff486d7f6f2',
                    ],
                    [
                        'id' => '660c1473-ad37-4aae-afd4-1ff486d7f6f2',
                    ],
                ],
            ],
            [
                'dependencies' => [
                    'type' => 'array',
                ],
            ],
        );
    }

    /**
     * If the properties key is missing, then a logic exception must be thrown.
     */
    public function testThrowNewLogicExceptionWhenThePropertiesIsNotFilledForObjectType(): void
    {
        $this->expectException(\LogicException::class);
        $this->expectExceptionMessage('The key properties should be define for the type object.');

        $this->validator->validate(
            [
                'dependency' => [
                    'id' => '660c1473-ad37-4aae-afd4-1ff486d7f6f2',
                ],
            ],
            [
                'dependency' => [
                    'type' => 'object',
                ],
            ],
        );
    }

    // @todo test nullable: true and false scalaire / object / array
    // attention null est différent de non présent
    // @todo test si je dis que c'est un objet mais que ça n'en est pas un
    // @todo test si je dis que c'est un array mais que ça n'en est pas un
    // @todo test si c'est un enum, doit faire parti de la liste associée

    // @todo enum, si une valeur string est passé pour un array enum boom

    /**
     * If a type key is missing, then a logic exception must be thrown.
     */
    public function testThrowNewLogicExceptionWhenTheTypeIsNotFilled(): void
    {
        $this->expectException(\LogicException::class);
        $this->expectExceptionMessage('The type key is missing for the key example1.');

        $this->validator->validate(
            $this->getSimpleScalarExample(),
            [
                'example1' => [],
                'example2' => [],
                'example3' => [],
            ],
        );
    }

    /**
     * If a type does not exist, then a logic exception must be thrown.
     */
    public function testThrowNewLogicExceptionWhenTheTypeIsNotSupported(): void
    {
        $this->expectException(\LogicException::class);
        $this->expectExceptionMessage(
            \sprintf(
                'The riri type doesn\'t exist. The supported types are %s.',
                \json_encode(
                    SupportedTypeEnum::cases(),
                ),
            ),
        );

        $this->validator->validate(
            $this->getSimpleScalarExample(),
            [
                'example1' => [
                    'type' => 'riri',
                ],
                'example2' => [
                    'type' => 'fifi',
                ],
                'example3' => [
                    'type' => 'loulou',
                ],
            ],
        );
    }

    /**
     * If the type value is not an integer / number and there is a multipleOf property, then a logic exception must be thrown.
     */
    public function testThrowNewLogicExceptionWhenTheTypeOfMultipleOfIsNotAnIntegerOrNumber(): void
    {
        $this->expectException(\LogicException::class);
        $this->expectExceptionMessage('The `multipleOf` openApi property only works with integer and number types.');

        $this->validator->validate(
            [
                'example' => '6',
            ],
            [
                'example' => [
                    'type' => 'string',
                    'multipleOf' => 3,
                ],
            ],
        );
    }

    /**
     * If the value is not an integer / number and there is a multipleOf property, then a logic exception must be thrown.
     */
    public function testThrowNewLogicExceptionWhenTheValueOfMultipleOfIsNotAnIntegerOrNumber(): void
    {
        $this->expectException(\LogicException::class);
        $this->expectExceptionMessage('The `multipleOf` value must be an integer or number.');

        $this->validator->validate(
            [
                'example' => '6',
            ],
            [
                'example' => [
                    'type' => 'number',
                    'multipleOf' => '3',
                ],
            ],
        );
    }

    /**
     * When an url string field is correctly filled in, it must not return an error.
     * If the field is of another type, then an error must be returned.
     */
    public function testUrlFormat(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'badUrl1',
                    'value' => 'www.vetinweb.fr',
                    'message' => 'format.url',
                ],
                [
                    'key' => 'badUrl2',
                    'value' => 'vetinweb.fr',
                    'message' => 'format.url',
                ],
                [
                    'key' => 'badUrl3',
                    'value' => 'vetinweb',
                    'message' => 'format.url',
                ],
            ],
            $this->validator->validate(
                [
                    'goodHttpUrl' => 'http://www.vetinweb.fr',
                    'goodHttpUrl2' => 'http://www.vetinweb.fr?q=1',
                    'goodHttpsUrl' => 'https://www.vetinweb.fr',
                    'goodHttpsUrl2' => 'https://www.vetinweb.fr?q=1',
                    'badUrl1' => 'www.vetinweb.fr',
                    'badUrl2' => 'vetinweb.fr',
                    'badUrl3' => 'vetinweb',
                ],
                [
                    'goodHttpUrl' => [
                        'type' => 'string',
                        'format' => 'url',
                    ],
                    'goodHttpUrl2' => [
                        'type' => 'string',
                        'format' => 'url',
                    ],
                    'goodHttpsUrl' => [
                        'type' => 'string',
                        'format' => 'url',
                    ],
                    'goodHttpsUrl2' => [
                        'type' => 'string',
                        'format' => 'url',
                    ],
                    'badUrl1' => [
                        'type' => 'string',
                        'format' => 'url',
                    ],
                    'badUrl2' => [
                        'type' => 'string',
                        'format' => 'url',
                    ],
                    'badUrl3' => [
                        'type' => 'string',
                        'format' => 'url',
                    ],
                ],
            ),
        );
    }

    /**
     * When uuid array field is correctly filled in uuid format, it must not return an error.
     * Else an error must be returned.
     */
    public function testUuidArrayFormat(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'badUuidExample[0].id',
                    'value' => 'bad',
                    'message' => 'format.uuid',
                ],
                [
                    'key' => 'badUuidExample[1].id',
                    'value' => 'bad too',
                    'message' => 'format.uuid',
                ],
                [
                    'key' => 'badUuidExample2',
                    'value' => '',
                    'message' => 'type.array',
                ],
                [
                    'key' => 'badUuidExample3[0].id',
                    'value' => 'bad',
                    'message' => 'format.uuid',
                ],
                [
                    'key' => 'badUuidExample3[1].id',
                    'value' => 'bad too',
                    'message' => 'format.uuid',
                ],
            ],
            $this->validator->validate(
                [
                    'goodExample' => [
                        0 => [
                            'id' => '660c1473-ad37-4aae-afd4-1ff486d7f6f2',
                        ],
                        1 => [
                            'id' => '66ac1473-ad37-4aae-afd4-1ff486d7f6f4',
                        ],
                    ],
                    'badUuidExample' => [
                        0 => [
                            'id' => 'bad',
                        ],
                        1 => [
                            'id' => 'bad too',
                        ],
                    ],
                    'badUuidExample2' => '',
                    'badUuidExample3' => [
                        0 => [
                            'id' => 'bad',
                        ],
                        1 => [
                            'id' => 'bad too',
                        ],
                    ],
                ],
                [
                    'goodExample' => [
                        'type' => 'array',
                        'items' => [
                            'properties' => [
                                'id' => [
                                    'format' => 'uuid',
                                    'type' => 'string',
                                ],
                            ],
                            'type' => 'object',
                        ],
                    ],
                    'badUuidExample' => [
                        'type' => 'array',
                        'items' => [
                            'properties' => [
                                'id' => [
                                    'format' => 'uuid',
                                    'type' => 'string',
                                ],
                            ],
                            'type' => 'object',
                        ],
                    ],
                    'badUuidExample2' => [
                        'type' => 'array',
                        'items' => [
                            'properties' => [
                                'id' => [
                                    'format' => 'uuid',
                                    'type' => 'string',
                                ],
                            ],
                            'type' => 'object',
                        ],
                    ],
                    'badUuidExample3' => [
                        'type' => 'array',
                        'items' => [
                            'properties' => [
                                'id' => [
                                    'format' => 'uuid',
                                    'type' => 'string',
                                ],
                            ],
                            'type' => 'object',
                        ],
                    ],
                ],
            ),
        );
    }

    /**
     * When string field is correctly filled in uuid format, it must not return an error.
     * Else an error must be returned.
     */
    public function testUuidFormat(): void
    {
        $this->assertSame(
            [
                [
                    'key' => 'badUuidExample',
                    'value' => '660c1473&ad37!4aae5afd491ff486d7f6f2',
                    'message' => 'format.uuid',
                ],
            ],
            $this->validator->validate(
                [
                    'uuidExample' => '660c1473-ad37-4aae-afd4-1ff486d7f6f2',
                    'badUuidExample' => '660c1473&ad37!4aae5afd491ff486d7f6f2',
                ],
                [
                    'uuidExample' => [
                        'type' => 'string',
                        'format' => 'uuid',
                    ],
                    'badUuidExample' => [
                        'type' => 'string',
                        'format' => 'uuid',
                    ],
                ],
            ),
        );
    }

    /**
     * @return array<string, mixed>
     */
    private function getSimpleScalarExample(): array
    {
        return [
            'example1' => "I\'am a string.",
            'example2' => 0,
            'example3' => true,
        ];
    }
}
