<?php

declare(strict_types=1);

namespace App\Module\Commercial\Infrastructure\ApiPlatform\State\Processor;

use ApiPlatform\Metadata\DeleteOperationInterface;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Module\Commercial\Application\Service\ClientFieldNormalizer;
use App\Module\Commercial\Domain\Entity\Client;
use App\Module\Commercial\Domain\Entity\ClientAddress;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * Processor d'ecriture de la sous-ressource Adresse d'un client (M1, § 4.5).
 *
 * Sequence :
 *  - POST / PATCH : normalisation serveur du billingEmail en lowercase (RG-1.21)
 *    via le ClientFieldNormalizer partage. Les autres regles de l'onglet Adresse
 *    sont deja garanties en amont : RG-1.09 (code postal) et RG-1.10 (>= 1 site)
 *    par des contraintes Assert sur l'entite, RG-1.06/07/08/11 par des CHECK BDD.
 *  - DELETE : aucune regle metier specifique (suppression physique directe).
 *
 * La security de l'operation (commercial.clients.manage) est deja appliquee par
 * API Platform, de meme que la validation Symfony des contraintes d'attribut.
 *
 * @implements ProcessorInterface<ClientAddress, null|ClientAddress>
 */
final class ClientAddressProcessor implements ProcessorInterface
{
    public function __construct(
        #[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')]
        private readonly ProcessorInterface $persistProcessor,
        #[Autowire(service: 'api_platform.doctrine.orm.state.remove_processor')]
        private readonly ProcessorInterface $removeProcessor,
        private readonly ClientFieldNormalizer $normalizer,
        private readonly EntityManagerInterface $em,
    ) {}

    public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
    {
        if (!$data instanceof ClientAddress) {
            return $this->persistProcessor->process($data, $operation, $uriVariables, $context);
        }

        if ($operation instanceof DeleteOperationInterface) {
            return $this->removeProcessor->process($data, $operation, $uriVariables, $context);
        }

        $this->linkParent($data, $uriVariables);
        $this->normalize($data);

        return $this->persistProcessor->process($data, $operation, $uriVariables, $context);
    }

    /**
     * Rattache l'adresse au client parent de la sous-ressource POST
     * (/clients/{clientId}/addresses) : la relation n'est pas peuplee
     * automatiquement par le Link sur une ecriture. Sur PATCH, no-op.
     */
    private function linkParent(ClientAddress $address, array $uriVariables): void
    {
        if (null !== $address->getClient()) {
            return;
        }

        $clientId = $uriVariables['clientId'] ?? null;
        if (null === $clientId) {
            return;
        }

        $client = $clientId instanceof Client
            ? $clientId
            : $this->em->getRepository(Client::class)->find($clientId);

        // read:false sur le POST : sans stade lecture, un parent introuvable n'est
        // plus intercepte en amont -> 404 explicite (sinon 500 au persist sur la
        // contrainte client_id NOT NULL).
        if (!$client instanceof Client) {
            throw new NotFoundHttpException('Client introuvable.');
        }

        $address->setClient($client);
    }

    /**
     * Normalisation serveur (RG-1.21) : email de facturation en minuscules. La
     * methode est null-safe — une adresse non facturable (billingEmail null)
     * reste null.
     */
    private function normalize(ClientAddress $address): void
    {
        $address->setBillingEmail($this->normalizer->normalizeEmail($address->getBillingEmail()));
        $address->setBillingEmailSecondary($this->normalizer->normalizeEmail($address->getBillingEmailSecondary()));
    }
}
