<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\DocGeneratorBundle\Test;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
 * @template T of object
 */
abstract class DocGenNormalizerTestAbstract extends KernelTestCase
{
    public function testNullValueHasSameKeysAsNull(): void
    {
        $normalizedObject = $this->getNormalizer()->normalize($this->provideNotNullObject(), 'docgen', [
            AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => $this->provideDocGenExpectClass(),
        ]);
        $nullNormalizedObject = $this->getNormalizer()->normalize(null, 'docgen', [
            AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => $this->provideDocGenExpectClass(),
        ]);

        self::assertEqualsCanonicalizing(array_keys($normalizedObject), array_keys($nullNormalizedObject));
        self::assertArrayHasKey('isNull', $nullNormalizedObject, 'each object must have an "isNull" key');
        self::assertTrue($nullNormalizedObject['isNull'], 'isNull key must be true for null objects');
        self::assertFalse($normalizedObject['isNull'], 'isNull key must be false for null objects');

        foreach ($normalizedObject as $key => $value) {
            if (in_array($key, ['isNull', 'type'])) {
                continue;
            }

            if (is_array($value)) {
                if (array_is_list($value)) {
                    self::assertEquals([], $nullNormalizedObject[$key], "list must be serialized as an empty array, in {$key}");
                } else {
                    self::assertEqualsCanonicalizing(array_keys($value), array_keys($nullNormalizedObject[$key]), "sub-object must have the same keys, in {$key}");
                }
            } elseif (is_string($value)) {
                self::assertEquals('', $nullNormalizedObject[$key], 'strings must be ');
            }
        }
    }

    /**
     * @return T
     */
    abstract public function provideNotNullObject(): object;

    /**
     * @return class-string<T>
     */
    abstract public function provideDocGenExpectClass(): string;

    abstract public function getNormalizer(): NormalizerInterface;
}
