<?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 Serializer\Normalizer;

use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\Relationships\Relation;
use Chill\PersonBundle\Entity\Relationships\Relationship;
use Chill\PersonBundle\Serializer\Normalizer\RelationshipDocGenNormalizer;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
 * @internal
 *
 * @coversNothing
 */
final class RelationshipDocGenNormalizerTest extends TestCase
{
    use ProphecyTrait;

    public function testNormalizeRelationshipNull()
    {
        $relationship = null;

        $normalizer = $this->buildNormalizer();

        $this->assertTrue($normalizer->supportsNormalization($relationship, 'docgen', [
            'docgen:expects' => Relationship::class,
        ]));
        $this->assertFalse($normalizer->supportsNormalization($relationship, 'docgen', [
            'docgen:expects' => Person::class,
        ]));

        $actual = $normalizer->normalize($relationship, 'docgen', [
            'docgen:expects' => Relationship::class,
        ]);
        $this->assertIsArray($actual);
        $this->assertEqualsCanonicalizing(
            ['fromPerson', 'toPerson', 'id', 'relationId', 'text', 'opposite'],
            array_keys($actual),
            'check that the expected keys are present'
        );
    }

    public function testNormalizeRelationshipWithCounterPart()
    {
        $relationship = new Relationship();
        $relationship
            ->setFromPerson($person1 = new Person())
            ->setToPerson($person2 = new Person())
            ->setRelation(
                (new Relation())->setTitle(['fr' => 'title'])
                    ->setReverseTitle(['fr' => 'reverse title'])
            )
            ->setReverse(false);

        $normalizer = $this->buildNormalizer();

        $this->assertTrue($normalizer->supportsNormalization($relationship, 'docgen', []));
        $this->assertFalse($normalizer->supportsNormalization($person1, 'docgen', []));

        $actual = $normalizer->normalize($relationship, 'docgen', [
            'docgen:expects' => Relationship::class,
            'docgen:relationship:counterpart' => $person1,
        ]);

        $this->assertIsArray($actual);
        $this->assertEqualsCanonicalizing(
            ['fromPerson', 'toPerson', 'id', 'relationId', 'text', 'opposite'],
            array_keys($actual),
            'check that the expected keys are present'
        );
        $this->assertEquals(spl_object_hash($person2), $actual['opposite']['hash']);
    }

    public function testNormalizeRelationshipWithoutCounterPart()
    {
        $relationship = new Relationship();
        $relationship
            ->setFromPerson($person1 = new Person())
            ->setToPerson($person2 = new Person())
            ->setRelation(
                (new Relation())->setTitle(['fr' => 'title'])
                    ->setReverseTitle(['fr' => 'reverse title'])
            )
            ->setReverse(false);

        $normalizer = $this->buildNormalizer();

        $this->assertTrue($normalizer->supportsNormalization($relationship, 'docgen', []));
        $this->assertFalse($normalizer->supportsNormalization($person1, 'docgen', []));

        $actual = $normalizer->normalize($relationship, 'docgen', [
            'docgen:expects' => Relationship::class,
        ]);
        $this->assertIsArray($actual);
        $this->assertEqualsCanonicalizing(
            ['fromPerson', 'toPerson', 'id', 'relationId', 'text', 'opposite'],
            array_keys($actual),
            'check that the expected keys are present'
        );
        $this->assertEquals(null, $actual['opposite']);
    }

    private function buildNormalizer(): RelationshipDocGenNormalizer
    {
        $translatableStringHelper = $this->prophesize(TranslatableStringHelperInterface::class);
        $translatableStringHelper->localize(Argument::type('array'))->will(
            static fn ($args) => $args[0][array_keys($args[0])[0]]
        );

        $normalizer = new RelationshipDocGenNormalizer(
            $translatableStringHelper->reveal()
        );

        $normalizerManager = $this->prophesize(NormalizerInterface::class);
        $normalizerManager->supportsNormalization(Argument::any(), 'docgen', Argument::any())->willReturn(true);
        $normalizerManager->normalize(Argument::type(Relationship::class), 'docgen', Argument::any())
            ->will(static fn ($args) => $normalizer->normalize($args[0], $args[1], $args[2]));
        $normalizerManager->normalize(Argument::any(), 'docgen', Argument::any())->will(
            static function ($args) {
                if (null === $args[0]) {
                    return null;
                }

                if (is_iterable($args[0])) {
                    $r = [];

                    foreach ($args[0] as $i) {
                        $r[] = ['fake' => true, 'hash' => spl_object_hash($i)];
                    }

                    return $r;
                }

                if (\is_object($args[0])) {
                    return ['fake' => true, 'hash' => null !== $args[0] ? spl_object_hash($args[0]) : null];
                }

                return $args[0];
            }
        );
        $normalizer->setNormalizer($normalizerManager->reveal());

        return $normalizer;
    }
}
