<?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\SupplierRib;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * Processor d'ecriture de la sous-ressource RIB d'un fournisseur (M2, spec-back
 * § 4.5). Jumeau du ClientRibProcessor (M1), recentre sur le perimetre ERP-88.
 *
 * Sequence :
 *  - POST / PATCH : rattachement au fournisseur parent. Aucune normalisation
 *    specifique ; la validite de l'IBAN et du BIC est garantie par Assert\Iban /
 *    Assert\Bic sur l'entite (jouees en amont par API Platform). Aucun
 *    #[AuditIgnore] sur iban/bic : la tracabilite comptable est volontaire
 *    (decision M1 reportee, spec § 2.7).
 *  - DELETE : RG-2.08 — si le fournisseur est en reglement LCR, la suppression de
 *    son DERNIER RIB est refusee (409), car LCR exige au moins un RIB.
 *
 * La security de l'operation (commercial.suppliers.accounting.manage) est
 * appliquee par API Platform en amont : un utilisateur sans cette permission
 * recoit 403 sur POST/PATCH/DELETE avant d'atteindre ce processor — c'est le
 * niveau de gating renforce des donnees bancaires (distinct de manage, spec
 * § 4.5).
 *
 * @implements ProcessorInterface<SupplierRib, null|SupplierRib>
 */
final class SupplierRibProcessor 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 SupplierRib) {
            return $this->persistProcessor->process($data, $operation, $uriVariables, $context);
        }

        if ($operation instanceof DeleteOperationInterface) {
            $this->guardLastRibDeletionUnderLcr($data);

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

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

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

    /**
     * Rattache le RIB au fournisseur parent de la sous-ressource POST
     * (/suppliers/{supplierId}/ribs) : la relation n'est pas peuplee
     * automatiquement par le Link sur une ecriture. Sur PATCH, no-op.
     */
    private function linkParent(SupplierRib $rib, array $uriVariables): void
    {
        if (null !== $rib->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.');
        }

        $rib->setSupplier($supplier);
    }

    /**
     * RG-2.08 : un fournisseur dont le type de reglement est LCR doit conserver au
     * moins un RIB. La collection inclut le RIB en cours de suppression : un
     * effectif <= 1 signifie qu'il ne resterait aucun RIB -> 409. Pour tout autre
     * type de reglement, les RIBs sont optionnels (suppression libre).
     */
    private function guardLastRibDeletionUnderLcr(SupplierRib $rib): void
    {
        $supplier = $rib->getSupplier();
        if (null === $supplier) {
            return;
        }

        if ('LCR' === $supplier->getPaymentType()?->getCode() && $supplier->getRibs()->count() <= 1) {
            throw new ConflictHttpException(
                'Impossible de supprimer le dernier RIB : le type de règlement LCR exige au moins un RIB.',
            );
        }
    }
}
