<?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\Form\Type\DataTransformer;

use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserGroup;
use Chill\MainBundle\Serializer\Normalizer\DiscriminatedObjectDenormalizer;
use Chill\PersonBundle\Entity\Person;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\SerializerInterface;

class EntityToJsonTransformer implements DataTransformerInterface
{
    public function __construct(private readonly DenormalizerInterface $denormalizer, private readonly SerializerInterface $serializer, private readonly bool $multiple, private readonly string $type) {}

    public function reverseTransform($value)
    {
        if ('' === $value) {
            return $this->multiple ? [] : null;
        }

        if ($this->multiple && [] === $value) {
            return [];
        }

        $denormalized = json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR);

        if ($this->multiple) {
            if (null === $denormalized) {
                return [];
            }

            return array_map(
                fn ($item) => $this->denormalizeOne($item),
                $denormalized
            );
        }

        return $this->denormalizeOne($denormalized);
    }

    /**
     * @param User|User[] $value
     */
    public function transform($value): string
    {
        if (null === $value) {
            return $this->multiple ? 'null' : '[]';
        }

        return $this->serializer->serialize($value, 'json', [
            AbstractNormalizer::GROUPS => ['read'],
        ]);
    }

    private function denormalizeOne(array|string $item)
    {
        if ('me' === $item) {
            return $item;
        }
        if (!\array_key_exists('type', $item)) {
            throw new TransformationFailedException('the key "type" is missing on element');
        }

        if (!\array_key_exists('id', $item)) {
            throw new TransformationFailedException('the key "id" is missing on element');
        }

        $class = match ($this->type) {
            'user' => User::class,
            'person' => Person::class,
            'thirdparty' => ThirdParty::class,
            'user_group' => UserGroup::class,
            'user_group_or_user' => DiscriminatedObjectDenormalizer::TYPE,
            default => throw new \UnexpectedValueException('This type is not supported'),
        };

        $context = [AbstractNormalizer::GROUPS => ['read']];

        if ('user_group_or_user' === $this->type) {
            $context[DiscriminatedObjectDenormalizer::ALLOWED_TYPES] = [UserGroup::class, User::class];
        }

        return
            $this->denormalizer->denormalize(
                ['type' => $item['type'], 'id' => $item['id']],
                $class,
                'json',
                $context,
            );

    }
}
