<?php

declare(strict_types=1);

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

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Module\Core\Domain\Entity\User;
use App\Module\Core\Domain\Exception\LastAdminProtectionException;
use App\Module\Core\Domain\Security\AdminHeadcountGuardInterface;
use LogicException;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

/**
 * Processor dedie a l'operation `DELETE /api/users/{id}`.
 *
 * Delegue la suppression au RemoveProcessor Doctrine decore apres avoir
 * applique la garde "dernier admin global" : si l'utilisateur cible est
 * le seul admin restant sur l'instance, la suppression est refusee pour
 * preserver l'invariant "au moins un administrateur reste toujours".
 *
 * La garde est portee par AdminHeadcountGuard (domaine), partagee avec
 * UserRbacProcessor qui gere le meme invariant sur le chemin PATCH /rbac.
 *
 * @implements ProcessorInterface<User, User>
 */
final class UserProcessor implements ProcessorInterface
{
    public function __construct(
        #[Autowire(service: 'api_platform.doctrine.orm.state.remove_processor')]
        private readonly ProcessorInterface $removeProcessor,
        private readonly AdminHeadcountGuardInterface $adminHeadcountGuard,
    ) {}

    public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
    {
        if (!$data instanceof User) {
            // Ce processor est wire exclusivement sur l'operation Delete de User.
            // Si on arrive ici avec un autre type, c'est une misconfiguration.
            throw new LogicException(sprintf(
                'UserProcessor attend une instance de %s, %s recu.',
                User::class,
                get_debug_type($data),
            ));
        }

        // Garde dernier admin global : on ne verifie que si on supprime
        // effectivement un admin. La suppression d'un user standard n'a
        // aucun impact sur le compteur d'administrateurs.
        if ($data->isAdmin()) {
            try {
                $this->adminHeadcountGuard->ensureAtLeastOneAdminRemainsAfterDeletion($data);
            } catch (LastAdminProtectionException $exception) {
                throw new BadRequestHttpException($exception->getMessage(), $exception);
            }
        }

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