<?php

declare(strict_types=1);

namespace App\Shared\Infrastructure\ApiPlatform\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Shared\Domain\Exception\FileUploadException;
use App\Shared\Infrastructure\Upload\FileUploader;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
use Symfony\Component\Security\Core\User\UserInterface;
use Throwable;

/**
 * Processor d'ecriture de l'upload generique (POST /api/uploaded_documents).
 *
 * L'operation Post est en `deserialize: false` : le binaire n'est pas mappe sur
 * l'entite. Ce processor lit le fichier multipart de la requete (champ « file »),
 * delegue au FileUploader (validation MIME server-side, bornage taille, checksum,
 * ecriture disque), positionne l'auteur (created_by) puis persiste via le
 * processor Doctrine standard. Le retour est l'entite, qu'API Platform serialise
 * en JSON-LD (avec son @id / IRI).
 *
 * Mapping des erreurs :
 *  - fichier absent -> 422 ;
 *  - MIME hors whitelist / fichier trop volumineux (FileUploadException) -> 422.
 *
 * Si la persistance Doctrine echoue APRES l'ecriture disque, le fichier physique
 * deja deplace est supprime (compensation) pour ne pas laisser de binaire orphelin.
 *
 * @implements ProcessorInterface<mixed, mixed>
 */
final class UploadedDocumentProcessor implements ProcessorInterface
{
    public function __construct(
        #[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')]
        private readonly ProcessorInterface $persistProcessor,
        private readonly FileUploader $fileUploader,
        private readonly RequestStack $requestStack,
        private readonly Security $security,
    ) {}

    public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
    {
        $request = $this->requestStack->getCurrentRequest();
        $file    = $request?->files->get('file');

        if (!$file instanceof UploadedFile) {
            throw new UnprocessableEntityHttpException('Aucun fichier fourni (champ « file » attendu).');
        }

        try {
            $document = $this->fileUploader->upload($file);
        } catch (FileUploadException $e) {
            // MIME hors whitelist ou fichier trop volumineux -> 422 avec le
            // message metier explicite porte par l'exception.
            throw new UnprocessableEntityHttpException($e->getMessage(), $e);
        }

        $user = $this->security->getUser();
        if ($user instanceof UserInterface) {
            $document->setCreatedBy($user);
        }

        try {
            return $this->persistProcessor->process($document, $operation, $uriVariables, $context);
        } catch (Throwable $e) {
            // La persistance a echoue APRES l'ecriture disque (erreur DB, FK...) :
            // on supprime le fichier orphelin pour ne pas le laisser sans ligne
            // uploaded_document correspondante, puis on relaie l'erreur.
            $this->fileUploader->remove($document);

            throw $e;
        }
    }
}
