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

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;

class DoctrineExistingEntityNormalizer implements DenormalizerInterface
{
    public function __construct(private readonly EntityManagerInterface $em, private readonly ClassMetadataFactoryInterface $serializerMetadataFactory) {}

    public function denormalize($data, $type, $format = null, array $context = [])
    {
        if (\array_key_exists(AbstractNormalizer::OBJECT_TO_POPULATE, $context)) {
            return $context[AbstractNormalizer::OBJECT_TO_POPULATE];
        }

        return $this->em->getRepository($type)
            ->find($data['id']);
    }

    public function supportsDenormalization($data, $type, $format = null)
    {
        if (false === \is_array($data)) {
            return false;
        }

        if (false === \array_key_exists('id', $data)) {
            return false;
        }

        if (false === $this->em->getClassMetadata($type) instanceof ClassMetadata) {
            return false;
        }

        // does have serializer metadata, and class discriminator ?
        if ($this->serializerMetadataFactory->hasMetadataFor($type)) {
            $classDiscriminator = $this->serializerMetadataFactory
                ->getMetadataFor($type)->getClassDiscriminatorMapping();

            if ($classDiscriminator) {
                $typeProperty = $classDiscriminator->getTypeProperty();

                // check that only 2 keys
                // that the second key is property
                // and that the type match the class for given type property
                return 2 === \count($data)
                    && \array_key_exists($typeProperty, $data)
                    && $classDiscriminator->getClassForType($data[$typeProperty]) === $type;
            }
        }

        // we do not have any class discriminator. Check that the id is the only one key
        return 1 === \count($data);
    }
}
