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

use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\TicketBundle\Entity\EmergencyStatusEnum;
use Chill\TicketBundle\Entity\Motive;
use Chill\TicketBundle\Serializer\Normalizer\MotiveNormalizer;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
 * @internal
 *
 * @covers \Chill\TicketBundle\Serializer\Normalizer\MotiveNormalizer
 */
final class MotiveNormalizerTest extends TestCase
{
    public function testNormalizeReadBasic(): void
    {
        $motive = new Motive();
        $motive->setLabel(['fr' => 'Logement', 'en' => 'Housing']);
        // active is true by default

        $normalizer = new MotiveNormalizer();
        $normalizer->setNormalizer($this->buildDummyNormalizer());

        $actual = $normalizer->normalize($motive, 'json', ['groups' => 'read']);

        self::assertSame('ticket_motive', $actual['type']);
        self::assertNull($actual['id']);
        self::assertSame(['fr' => 'Logement', 'en' => 'Housing'], $actual['label']);
        self::assertTrue($actual['active']);
        // no extended fields here
        self::assertArrayNotHasKey('makeTicketEmergency', $actual);
        self::assertArrayNotHasKey('supplementaryComments', $actual);
        self::assertArrayNotHasKey('storedObjects', $actual);
        self::assertArrayNotHasKey('children', $actual);
    }

    public function testNormalizeExtended(): void
    {
        $motive = new Motive();
        $motive->setLabel(['fr' => 'Financier']);
        $motive->setMakeTicketEmergency(EmergencyStatusEnum::YES);
        $motive->addSupplementaryComment(['label' => 'Justifier le revenu']);
        $motive->addStoredObject(new StoredObject('pending'));

        $normalizer = new MotiveNormalizer();
        $normalizer->setNormalizer($this->buildDummyNormalizer());

        $actual = $normalizer->normalize($motive, 'json', ['groups' => ['read', 'read:extended']]);

        self::assertSame('ticket_motive', $actual['type']);
        self::assertSame(['fr' => 'Financier'], $actual['label']);
        self::assertSame(EmergencyStatusEnum::YES, $actual['makeTicketEmergency']);
        self::assertSame([
            ['label' => 'Justifier le revenu'],
        ], $actual['supplementaryComments']);
        self::assertSame([
            ['stored_object'],
        ], $actual['storedObjects']);
    }

    public function testNormalizeParentToChildren(): void
    {
        $parent = new Motive();
        $parent->setLabel(['fr' => 'Parent']);
        $child1 = new Motive();
        $child1->setLabel(['fr' => 'Enfant 1']);
        $child2 = new Motive();
        $child2->setLabel(['fr' => 'Enfant 2']);

        // build relation
        $child1->setParent($parent);
        $child2->setParent($parent);

        $normalizer = new MotiveNormalizer();
        $normalizer->setNormalizer($this->buildDummyNormalizer());

        $actual = $normalizer->normalize($parent, 'json', ['groups' => [MotiveNormalizer::GROUP_PARENT_TO_CHILDREN]]);

        // children must be normalized by the injected normalizer and parent not exposed
        self::assertArrayHasKey('children', $actual);
        self::assertSame([
            ['motive' => 'normalized'],
            ['motive' => 'normalized'],
        ], $actual['children']);
        self::assertArrayNotHasKey('parent', $actual);
    }

    public function testNormalizeChildrenToParent(): void
    {
        $parent = new Motive();
        $parent->setLabel(['fr' => 'Parent']);
        $child = new Motive();
        $child->setLabel(['fr' => 'Enfant']);
        $child->setParent($parent);

        $normalizer = new MotiveNormalizer();
        $normalizer->setNormalizer($this->buildDummyNormalizer());

        $actual = $normalizer->normalize($child, 'json', ['groups' => ['read', MotiveNormalizer::GROUP_CHILDREN_TO_PARENT]]);

        // parent must be normalized by the injected normalizer and children not exposed
        self::assertArrayHasKey('parent', $actual);
        self::assertSame(['motive' => 'normalized'], $actual['parent']);
        self::assertArrayNotHasKey('children', $actual);
    }

    public function testSupportsAndSupportedTypes(): void
    {
        $motive = new Motive();
        $normalizer = new MotiveNormalizer();

        self::assertTrue($normalizer->supportsNormalization($motive, 'json'));
        self::assertFalse($normalizer->supportsNormalization(new \stdClass(), 'json'));

        $supported = $normalizer->getSupportedTypes('json');
        self::assertArrayHasKey(Motive::class, $supported);
        self::assertTrue($supported[Motive::class]);
    }

    private function buildDummyNormalizer(): NormalizerInterface
    {
        return new class () implements NormalizerInterface {
            public function normalize($object, ?string $format = null, array $context = []): array
            {
                if ($object instanceof StoredObject) {
                    return ['stored_object'];
                }
                if ($object instanceof Motive) {
                    return ['motive' => 'normalized'];
                }

                return ['normalized'];
            }

            public function supportsNormalization($data, ?string $format = null): bool
            {
                return true;
            }
        };
    }
}
