<?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\Client;
use App\Module\Commercial\Domain\Entity\ClientRib;
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 client (M1, § 4.5).
 *
 * Sequence :
 *  - POST / PATCH : 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 Matthieu 29/05, spec § 6.1).
 *  - DELETE : RG-1.13 — si le client 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.clients.accounting.manage) est deja
 * appliquee par API Platform en amont : un utilisateur sans cette permission
 * recoit 403 sur POST/PATCH/DELETE avant d'atteindre ce processor.
 *
 * @implements ProcessorInterface<ClientRib, null|ClientRib>
 */
final class ClientRibProcessor 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 ClientRib) {
            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 client parent de la sous-ressource POST
     * (/clients/{clientId}/ribs) : la relation n'est pas peuplee automatiquement
     * par le Link sur une ecriture. Sur PATCH, no-op.
     */
    private function linkParent(ClientRib $rib, array $uriVariables): void
    {
        if (null !== $rib->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.');
        }

        $rib->setClient($client);
    }

    /**
     * RG-1.13 : un client 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(ClientRib $rib): void
    {
        $client = $rib->getClient();
        if (null === $client) {
            return;
        }

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