<?php

namespace NoahVet\Reef\Infrastructure\Serialization\Json;

use NoahVet\Reef\Domain\Serialization\Json\JsonSerializerFacadeInterface;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class JsonSerializerFacade implements JsonSerializerFacadeInterface
{
    public function __construct(
        protected readonly JsonSerializerFactoryInterface $serializerFactory,
    ) {
    }

    /**
     * @param list<DenormalizerInterface|NormalizerInterface> $normalizers
     * @param array<string, mixed>                            $context
     *
     * @return array<mixed>|string|int|float|bool|\ArrayObject<string, mixed>|null
     *
     * @throws ExceptionInterface
     */
    #[\Override]
    public function normalize(
        mixed $data,
        array $normalizers = [],
        array $context = [],
    ): array|string|int|float|bool|\ArrayObject|null {
        return $this->serializerFactory
            ->createSerializer(normalizers: $normalizers)
            ->normalize(data: $data, format: 'json', context: $context)
        ;
    }

    /**
     * @template T of object
     *
     * @param class-string<T>                                 $type
     * @param list<DenormalizerInterface|NormalizerInterface> $normalizers
     *
     * @return T
     *
     * @throws ExceptionInterface
     */
    #[\Override]
    public function deserialize(
        string $data,
        string $type,
        array $normalizers = [],
    ): object {
        /** @var T $model */
        $model = $this->serializerFactory
            ->createSerializer(normalizers: $normalizers)
            ->deserialize(data: $data, type: $type, format: 'json')
        ;

        return $model;
    }

    /**
     * @template T of object
     *
     * @param array<string, mixed>                            $data
     * @param class-string<T>                                 $type
     * @param list<DenormalizerInterface|NormalizerInterface> $normalizers
     * @param array<string, mixed>                            $context
     * @param list<string>                                    $ignoredAttributes
     *
     * @return T
     *
     * @throws ExceptionInterface
     */
    #[\Override]
    public function denormalize(
        array $data,
        string $type,
        array $normalizers = [],
        array $context = [],
        array $ignoredAttributes = [],
        ?object $objectToPopulate = null,
    ): object {
        if ([] !== $ignoredAttributes) {
            $context['ignored_attributes'] = $ignoredAttributes;
        }

        if (null !== $objectToPopulate) {
            $context['object_to_populate'] = $objectToPopulate;
        }

        return $this->serializerFactory
            ->createSerializer(normalizers: $normalizers)
            ->denormalize(
                data: $data,
                type: $type,
                context: $context,
            )
        ;
    }
}
