<?php

declare(strict_types=1);

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

use NoahVet\Reef\Domain\Validation\V2\OpenApiPropertyValidator;
use NoahVet\Reef\Domain\Validation\V2\OpenApiValidator;
use NoahVet\Reef\Domain\Validation\V2\OpenApiValidatorInterface;
use PHPUnit\Framework\TestCase;

/**
 * This class respects the pattern:
 * testGiven<Context>When<Method>Then<ExpectedResult>
 */
final class NullableOpenApiValidatorTest extends TestCase
{
    private OpenApiValidatorInterface $subject;

    protected function setUp(): void
    {
        $this->subject = new OpenApiValidator([
            new OpenApiPropertyValidator(),
        ]);
    }

    /**
     * Given a nullable string value in model
     * And string is null in payload
     * When validate call
     * Then returns true.
     */
    public function testGivenNullableStringValueAndNullPayloadWhenValidateThenReturnsTrue(): void
    {
        $payload = [
            'clinicName' => null,
        ];

        $rules = [
            'clinicName' => [
                'nullable' => true,
                'type' => 'string',
            ],
        ];

        $this->assertTrue($this->subject->validate($payload, $rules));
    }

    /**
     * Given a nullable string value in model
     * And string is missing in payload
     * When validate call
     * Then returns true.
     */
    public function testGivenNullableStringValueAndMissingPayloadWhenValidateThenReturnsTrue(): void
    {
        $payload = [];

        $rules = [
            'clinicName' => [
                'nullable' => true,
                'type' => 'string',
            ],
        ];

        $this->assertTrue($this->subject->validate($payload, $rules));
    }

    /**
     * Given a nullable object value in model
     * And object is null in payload
     * When validate call
     * Then returns true.
     */
    public function testGivenNullableObjectValueAndNullPayloadWhenValidateThenReturnsTrue(): void
    {
        $payload = [
            'clinic' => null,
        ];

        $rules = [
            'clinic' => [
                'nullable' => true,
                'type' => 'object',
            ],
        ];

        $this->assertTrue($this->subject->validate($payload, $rules));
    }

    /**
     * Given a nullable object value in model
     * And object is missing in payload
     * When validate call
     * Then returns true.
     */
    public function testGivenNullableObjectValueAndMissingPayloadWhenValidateThenReturnsTrue(): void
    {
        $payload = [];

        $rules = [
            'clinic' => [
                'nullable' => true,
                'type' => 'object',
            ],
        ];

        $this->assertTrue($this->subject->validate($payload, $rules));
    }

    /**
     * Given a nullable array value in model
     * And array is null in payload
     * When validate call
     * Then returns true.
     */
    public function testGivenNullableArrayValueAndNullPayloadWhenValidateThenReturnsTrue(): void
    {
        $payload = [
            'clinic' => null,
        ];

        $rules = [
            'clinic' => [
                'nullable' => true,
                'type' => 'array',
            ],
        ];

        $this->assertTrue($this->subject->validate($payload, $rules));
    }

    /**
     * Given a nullable array value in model
     * And array is missing in payload
     * When validate call
     * Then returns true.
     */
    public function testGivenNullableArrayValueAndMissingPayloadWhenValidateThenReturnsTrue(): void
    {
        $payload = [];

        $rules = [
            'clinic' => [
                'nullable' => true,
                'type' => 'array',
            ],
        ];

        $this->assertTrue($this->subject->validate($payload, $rules));
    }

    /**
     * Given a nullable array object value in model
     * And array object is missing in payload
     * When validate call
     * Then returns true.
     */
    public function testGivenNullableArrayObjectValueAndMissingPayloadWhenValidateThenReturnsTrue(): void
    {
        $payload = [
            'clinic' => [],
        ];

        $rules = [
            'clinic' => [
                'type' => 'array',
                'items' => [
                    'type' => 'object',
                    'nullable' => 'true',
                ],
            ],
        ];

        $this->assertTrue($this->subject->validate($payload, $rules));
    }

    /**
     * Given a non-nullable string value in model
     * And string is null in payload
     * When validate call
     * Then returns false.
     */
    public function testGivenNonNullableStringValueAndNullPayloadWhenValidateThenReturnsFalse(): void
    {
        $payload = [
            'clinicName' => null,
        ];

        $rules = [
            'clinicName' => [
                'nullable' => false,
                'type' => 'string',
            ],
        ];

        $this->assertFalse($this->subject->validate($payload, $rules));
    }

    /**
     * Given a non-nullable string value in model
     * And string is missing in payload
     * When validate call
     * Then returns false.
     */
    public function testGivenNonNullableStringValueAndMissingPayloadWhenValidateThenReturnsFalse(): void
    {
        $payload = [];

        $rules = [
            'clinicName' => [
                'nullable' => false,
                'type' => 'string',
            ],
        ];

        $this->assertFalse($this->subject->validate($payload, $rules));
    }

    /**
     * Given a string value without nullable in model
     * And string is null in payload
     * When validate call
     * Then returns false.
     */
    public function testGivenStringValueWithoutNullableAndNullPayloadWhenValidateThenReturnsFalse(): void
    {
        $payload = [
            'clinicName' => null,
        ];

        $rules = [
            'clinicName' => [
                'type' => 'string',
            ],
        ];

        $this->assertFalse($this->subject->validate($payload, $rules));
    }

    /**
     * Given a string value without nullable in model
     * And string is missing in payload
     * When validate call
     * Then returns false.
     */
    public function testGivenStringValueWithoutNullableAndMissingPayloadWhenValidateThenReturnsFalse(): void
    {
        $payload = [];

        $rules = [
            'clinicName' => [
                'type' => 'string',
            ],
        ];

        $this->assertFalse($this->subject->validate($payload, $rules));
    }

    /**
     * Given a non-nullable object value in model
     * And object is null in payload
     * When validate call
     * Then returns false.
     */
    public function testGivenNonNullableObjectValueAndNullPayloadWhenValidateThenReturnsFalse(): void
    {
        $payload = [
            'clinic' => null,
        ];

        $rules = [
            'clinic' => [
                'nullable' => false,
                'type' => 'object',
            ],
        ];

        $this->assertFalse($this->subject->validate($payload, $rules));
    }

    /**
     * Given a non-nullable object value in model
     * And object is missing in payload
     * When validate call
     * Then returns false.
     */
    public function testGivenNonNullableObjectValueAndMissingPayloadWhenValidateThenReturnsFalse(): void
    {
        $payload = [];

        $rules = [
            'clinic' => [
                'nullable' => false,
                'type' => 'object',
            ],
        ];

        $this->assertFalse($this->subject->validate($payload, $rules));
    }

    /**
     * Given a non-nullable array value in model
     * And array is null in payload
     * When validate call
     * Then returns false.
     */
    public function testGivenNonNullableArrayValueAndNullPayloadWhenValidateThenReturnsFalse(): void
    {
        $payload = [
            'clinic' => null,
        ];

        $rules = [
            'clinic' => [
                'nullable' => false,
                'type' => 'array',
            ],
        ];

        $this->assertFalse($this->subject->validate($payload, $rules));
    }

    /**
     * Given a non-nullable array value in model
     * And array is missing in payload
     * When validate call
     * Then returns false.
     */
    public function testGivenNonNullableArrayValueAndMissingPayloadWhenValidateThenReturnsFalse(): void
    {
        $payload = [];

        $rules = [
            'clinic' => [
                'nullable' => false,
                'type' => 'array',
            ],
        ];

        $this->assertFalse($this->subject->validate($payload, $rules));
    }

    /**
     * Given a not specified object value in model
     * And object is null in payload
     * When validate call
     * Then returns false.
     */
    public function testGivenObjectValueWithoutNullableAndNullPayloadWhenValidateThenReturnsFalse(): void
    {
        $payload = [
            'clinic' => null,
        ];

        $rules = [
            'clinic' => [
                'type' => 'object',
            ],
        ];

        $this->assertFalse($this->subject->validate($payload, $rules));
    }

    /**
     * Given a not specified object value in model
     * And object is missing in payload
     * When validate call
     * Then returns false.
     */
    public function testGivenObjectValueWithoutNullableAndMissingPayloadWhenValidateThenReturnsFalse(): void
    {
        $payload = [];

        $rules = [
            'clinic' => [
                'type' => 'object',
            ],
        ];

        $this->assertFalse($this->subject->validate($payload, $rules));
    }

    /**
     * Given a not specified array value in model
     * And array is null in payload
     * When validate call
     * Then returns false.
     */
    public function testGivenArrayValueWithoutNullableAndNullPayloadWhenValidateThenReturnsFalse(): void
    {
        $payload = [
            'clinic' => null,
        ];

        $rules = [
            'clinic' => [
                'type' => 'array',
            ],
        ];

        $this->assertFalse($this->subject->validate($payload, $rules));
    }

    /**
     * Given a not specified array value in model
     * And array is missing in payload
     * When validate call
     * Then returns false.
     */
    public function testGivenArrayValueWithoutNullableAndMissingPayloadWhenValidateThenReturnsFalse(): void
    {
        $payload = [];

        $rules = [
            'clinic' => [
                'type' => 'array',
            ],
        ];

        $this->assertFalse($this->subject->validate($payload, $rules));
    }
}
