<?php

declare(strict_types=1);

namespace App\Module\Commercial\Infrastructure\ApiPlatform\Serializer;

use ApiPlatform\Metadata\IriConverterInterface;
use App\Shared\Domain\Contract\SiteInterface;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;

/**
 * Denormalise un IRI (`/api/sites/{id}`) vers le Site concret quand la propriete
 * cible est type-hintee par le contrat SiteInterface (ClientAddress::$sites).
 *
 * Meme mecanisme que CategoryReferenceDenormalizer : API Platform deduit le type
 * d'element de collection depuis le phpdoc `@var Collection<int, SiteInterface>`,
 * donc l'INTERFACE. Le serializer ne sait pas denormaliser un IRI vers une
 * interface (« Could not denormalize object of type SiteInterface[] ») ; on
 * resout l'IRI via l'IriConverter (qui retourne le Site mappe a la route) sans
 * importer la classe Site du module Sites — la regle ABSOLUE n°1 (pas d'import
 * cross-module) reste respectee : dependance au seul contrat Shared + API Platform.
 *
 * En lecture (normalisation), aucun probleme : l'objet reel EST un Site,
 * ressource a part entiere, serialise en IRI par le normalizer standard.
 */
final class SiteReferenceDenormalizer implements DenormalizerInterface
{
    public function __construct(
        private readonly IriConverterInterface $iriConverter,
    ) {}

    public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): ?SiteInterface
    {
        if (!is_string($data) || '' === $data) {
            return null;
        }

        // getResourceFromIri leve une exception sur IRI invalide -> 400, ce qui
        // est le comportement attendu pour une reference cassee.
        $resource = $this->iriConverter->getResourceFromIri($data);

        // IRI syntaxiquement valide mais pointant sur une autre ressource : on
        // refuse explicitement plutot que de retourner null silencieusement.
        if (!$resource instanceof SiteInterface) {
            throw new UnexpectedValueException(sprintf(
                'L\'IRI "%s" ne référence pas un site.',
                $data,
            ));
        }

        return $resource;
    }

    public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool
    {
        // Support base sur le seul type cible : l'ArrayDenormalizer (collection
        // `SiteInterface[]`) interroge le support en passant le TABLEAU complet
        // comme $data avant de deleguer element par element. Tester
        // is_string($data) ici casserait la chaine pour les collections.
        return SiteInterface::class === $type;
    }

    /**
     * @return array<class-string|string, bool>
     */
    public function getSupportedTypes(?string $format): array
    {
        return [SiteInterface::class => true];
    }
}
