<?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 Symfony\Component\Serializer\Exception\NotNormalizableValueException;
use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;

/**
 * Denormalize an object given a list of supported class.
 */
class DiscriminatedObjectDenormalizer implements ContextAwareDenormalizerInterface, DenormalizerAwareInterface
{
    use DenormalizerAwareTrait;

    /**
     * Should be present in context and contains an array of
     * allowed types.
     */
    final public const ALLOWED_TYPES = 'denormalize_multi.allowed_types';

    /**
     * The type to set for enabling this type.
     */
    final public const TYPE = '@multi';

    public function denormalize($data, $type, $format = null, array $context = [])
    {
        foreach ($context[self::ALLOWED_TYPES] as $localType) {
            if ($this->denormalizer->supportsDenormalization($data, $localType, $format)) {
                try {
                    return $this->denormalizer->denormalize($data, $localType, $format, $context);
                } catch (NotNormalizableValueException|RuntimeException $e) {
                    $lastException = $e;
                }
            }
        }

        throw new RuntimeException(sprintf('Could not find any denormalizer for those ALLOWED_TYPES: %s', \implode(', ', $context[self::ALLOWED_TYPES])), previous: $lastException ?? null);
    }

    public function supportsDenormalization($data, $type, $format = null, array $context = [])
    {
        if (self::TYPE !== $type) {
            return false;
        }

        if (0 === \count($context[self::ALLOWED_TYPES] ?? [])) {
            throw new \LogicException('The context should contains a list of
                allowed types');
        }

        foreach ($context[self::ALLOWED_TYPES] as $localType) {
            if ($this->denormalizer->supportsDenormalization($data, $localType, $format)) {
                return true;
            }
        }

        return false;
    }
}
