<?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\Domain\Entity\Supplier;
use App\Module\Commercial\Domain\Entity\SupplierAddress;
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 fournisseur (M2,
 * spec-back § 4.5). Jumeau du ClientAddressProcessor (M1), recentre sur le
 * perimetre ERP-88.
 *
 * Sequence :
 *  - POST / PATCH : rattachement au fournisseur parent. Aucune normalisation
 *    specifique (pas d'email de facturation au M2). Les regles de l'onglet
 *    Adresse sont garanties en amont par des contraintes sur l'entite, jouees
 *    par API Platform avant ce processor : RG-2.05 (code postal, Assert\Regex),
 *    RG-2.06 (>= 1 site, Assert\Count), RG-2.09 (type d'adresse, Assert\Choice +
 *    CHECK BDD), RG-2.10 (categorie de type FOURNISSEUR, Assert\Callback
 *    SupplierAddress::validateCategoryType).
 *  - DELETE : aucune regle metier specifique (suppression physique directe).
 *
 * La security de l'operation (commercial.suppliers.manage) est appliquee par API
 * Platform en amont, de meme que la validation Symfony des contraintes d'attribut.
 *
 * @implements ProcessorInterface<SupplierAddress, null|SupplierAddress>
 */
final class SupplierAddressProcessor 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 EntityManagerInterface $em,
    ) {}

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

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

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

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

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

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

        $supplier = $supplierId instanceof Supplier
            ? $supplierId
            : $this->em->getRepository(Supplier::class)->find($supplierId);

        // 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 supplier_id NOT NULL).
        if (!$supplier instanceof Supplier) {
            throw new NotFoundHttpException('Fournisseur introuvable.');
        }

        $address->setSupplier($supplier);
    }
}
