<?php

namespace App\Controller;

use App\Entity\Attestation;
use App\Entity\Devis;
use App\Entity\DocusealEvent;
use App\Entity\Echeancier;
use App\Repository\AttestationRepository;
use App\Repository\DevisRepository;
use App\Service\DocuSealService;
use App\Service\MailerService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Twig\Environment;

class WebhookDocuSealController extends AbstractController
{
    private const ATTESTATION_NOT_FOUND = 'Attestation not found';
    private const MONITOR_EMAIL = 'notification@e-cosplay.fr';

    #[Route('/webhooks/docuseal', name: 'app_webhook_docuseal', methods: ['POST'])]
    public function __invoke(
        Request $request,
        AttestationRepository $attestationRepository,
        DevisRepository $devisRepository,
        DocuSealService $docuSealService,
        MailerService $mailer,
        EntityManagerInterface $em,
        Environment $twig,
        #[Autowire(env: 'DOCUSEAL_WEBHOOKS_SECRET_HEADER')] string $secretHeader,
        #[Autowire(env: 'DOCUSEAL_WEBHOOKS_SECRET')] string $secret,
        #[Autowire('%kernel.project_dir%')] string $projectDir,
        #[Autowire(env: 'STRIPE_SK')] string $stripeSk = '',
    ): Response {
        $payload = $this->parseAndValidate($request, $secretHeader, $secret);
        if ($payload instanceof Response) {
            return $payload;
        }

        $eventType = $payload['event_type'] ?? '';
        $data = $payload['data'] ?? [];
        $submitterId = $data['id'] ?? null;
        $submissionId = $data['submission_id'] ?? null;
        $metadata = $data['metadata'] ?? [];
        $docType = $metadata['doc_type'] ?? null;

        // Log de l'evenement pour suivi temps reel (tous types : devis, attestation, etc.)
        if ('' !== $eventType && null !== $docType) {
            $em->persist(new DocusealEvent(
                $docType,
                $eventType,
                null !== $submissionId ? (int) $submissionId : null,
                null !== $submitterId ? (int) $submitterId : null,
                json_encode($payload),
            ));
            $em->flush();
        }

        // Dispatch par type de document
        if ('contrat' === $docType) {
            return $this->handleContratEvent($eventType, $data, $metadata, $mailer, $twig, $em, $projectDir);
        }

        if ('attestation_custom' === $docType) {
            return $this->handleAttestationCustomEvent($eventType, $data, $metadata, $em, $projectDir);
        }

        if ('eflex' === $docType) {
            return $this->handleEFlexEvent($eventType, $data, $metadata, $mailer, $twig, $em, $projectDir, $stripeSk);
        }

        if ('client_closure' === $docType) {
            return $this->handleClientClosureEvent($eventType, $data, $metadata, $mailer, $twig, $em);
        }

        if ('client_warning' === $docType) {
            return $this->handleClientWarningEvent($eventType, $data, $metadata, $mailer, $twig, $em);
        }

        if ('client_warning_reset' === $docType) {
            return $this->handleClientWarningResetEvent($eventType, $data, $metadata, $mailer, $twig, $em);
        }

        if ('echeancier_attestation' === $docType) {
            return $this->handleEcheancierAttestationEvent($eventType, $data, $metadata, $mailer, $twig, $em, $projectDir);
        }

        if ('echeancier' === $docType) {
            return $this->handleEcheancierEvent($eventType, $data, $metadata, $docuSealService, $mailer, $twig, $em, $projectDir, $stripeSk);
        }

        if ('devis' === $docType) {
            return $this->handleDevisEvent($eventType, $data, $metadata, $devisRepository, $docuSealService, $mailer, $twig, $em, $projectDir);
        }

        $this->syncSubmitterIdFromMetadata($docType, $submitterId, $metadata, $attestationRepository, $em);

        if ('attestation' !== $docType && null === $this->findAttestation($submitterId, $attestationRepository)) {
            return new JsonResponse(['status' => 'ignored', 'doc_type' => $docType]);
        }

        return match ($eventType) {
            'form.viewed' => $this->handleAttestationViewed($submitterId, $attestationRepository),
            'form.started' => $this->handleAttestationStarted($submitterId, $attestationRepository),
            'form.completed' => $this->handleAttestationCompleted($data, $attestationRepository, $mailer, $em, $twig, $projectDir),
            'form.declined' => $this->handleAttestationDeclined($data, $attestationRepository, $em),
            default => new JsonResponse(['status' => 'ignored', 'event' => $eventType]),
        };
    }

    /**
     * @param array<string, mixed> $data
     * @param array<string, mixed> $metadata
     */
    private function handleDevisEvent(
        string $eventType,
        array $data,
        array $metadata,
        DevisRepository $devisRepository,
        DocuSealService $docuSealService,
        MailerService $mailer,
        Environment $twig,
        EntityManagerInterface $em,
        string $projectDir,
    ): JsonResponse {
        // Resolution du devis via metadata.devis_id en priorite, sinon via submitter id
        $devis = null;
        $devisId = $metadata['devis_id'] ?? null;
        if (null !== $devisId) {
            $devis = $devisRepository->find((int) $devisId);
        }

        if (null === $devis) {
            $submitterId = $data['id'] ?? null;
            if (null !== $submitterId) {
                $devis = $devisRepository->findOneBy(['submissionId' => (string) $submitterId]);
            }
        }

        if (null === $devis) {
            return new JsonResponse(['status' => 'ignored', 'reason' => 'devis not found'], Response::HTTP_NOT_FOUND);
        }

        switch ($eventType) {
            case 'form.viewed':
            case 'form.started':
                // Juste tracer l'evenement (deja fait en amont via DocusealEvent)
                return new JsonResponse(['status' => 'ok', 'event' => $eventType, 'devis' => $devis->getOrderNumber()->getNumOrder()]);

            case 'form.completed':
                $devis->setState(Devis::STATE_ACCEPTED);
                $devis->setUpdatedAt(new \DateTimeImmutable());
                $em->flush();
                // Recupere le PDF signe et le certificat d'audit depuis DocuSeal (via Vich)
                $docuSealService->downloadSignedDevis($devis);

                // Notifications email : client + admin
                $this->sendDevisSignedNotifications($devis, $mailer, $twig, $projectDir);

                return new JsonResponse(['status' => 'ok', 'event' => 'completed', 'devis' => $devis->getOrderNumber()->getNumOrder()]);

            case 'form.declined':
                $devis->setState(Devis::STATE_REFUSED);
                $devis->setUpdatedAt(new \DateTimeImmutable());
                $reason = isset($data['decline_reason']) && '' !== $data['decline_reason']
                    ? (string) $data['decline_reason']
                    : null;
                if (null !== $reason) {
                    $devis->setRaisonMessage($reason);
                }
                $em->flush();

                // Notifications email : client + admin
                $this->sendDevisRefusedNotifications($devis, $reason, $mailer, $twig);

                return new JsonResponse(['status' => 'ok', 'event' => 'declined', 'devis' => $devis->getOrderNumber()->getNumOrder()]);

            default:
                return new JsonResponse(['status' => 'ignored', 'event' => $eventType]);
        }
    }

    /**
     * @param array<string, mixed> $data
     * @param array<string, mixed> $metadata
     */
    private function handleContratEvent(
        string $eventType,
        array $data,
        array $metadata,
        MailerService $mailer,
        Environment $twig,
        EntityManagerInterface $em,
        string $projectDir,
    ): JsonResponse {
        if ('form.completed' !== $eventType) {
            return new JsonResponse(['status' => 'ok', 'event' => $eventType, 'doc_type' => 'contrat']);
        }

        $contratId = $metadata['contrat_id'] ?? null;
        if (null === $contratId) {
            return new JsonResponse(['status' => 'ignored', 'reason' => 'contrat_id missing']);
        }

        $contrat = $em->getRepository(\App\Entity\Contrat::class)->find((int) $contratId);
        if (null === $contrat) {
            return new JsonResponse(['status' => 'ignored', 'reason' => 'contrat not found']);
        }

        // Telecharger les PDFs signes
        $tmpFiles = [];

        $documents = $data['documents'] ?? [];
        $pdfUrl = $documents[0]['url'] ?? null;
        if (null !== $pdfUrl) {
            $content = @file_get_contents($pdfUrl);
            if (false !== $content && str_starts_with($content, '%PDF')) {
                $tmp = tempnam(sys_get_temp_dir(), 'ctr_signed_').'.pdf';
                file_put_contents($tmp, $content);
                $contrat->setPdfSignedFile(new \Symfony\Component\HttpFoundation\File\UploadedFile($tmp, 'contrat-signe-'.$contrat->getReference().'.pdf', 'application/pdf', null, true));
                $tmpFiles[] = $tmp;
            }
        }

        $auditUrl = $data['audit_log_url'] ?? null;
        if (null !== $auditUrl) {
            $auditContent = @file_get_contents($auditUrl);
            if (false !== $auditContent) {
                $tmp = tempnam(sys_get_temp_dir(), 'ctr_audit_').'.pdf';
                file_put_contents($tmp, $auditContent);
                $contrat->setPdfAuditFile(new \Symfony\Component\HttpFoundation\File\UploadedFile($tmp, 'audit-'.$contrat->getReference().'.pdf', 'application/pdf', null, true));
                $tmpFiles[] = $tmp;
            }
        }

        $contrat->setState(\App\Entity\Contrat::STATE_SIGNED);
        $contrat->setSignedAt(new \DateTimeImmutable());
        $em->flush();

        foreach ($tmpFiles as $f) {
            @unlink($f);
        }

        // Pieces jointes
        $attachments = [];
        if (null !== $contrat->getPdfSigned()) {
            $signedPath = $projectDir.'/public/uploads/contrats/signed/'.$contrat->getPdfSigned();
            if (file_exists($signedPath)) {
                $attachments[] = ['path' => $signedPath, 'name' => 'contrat-signe-'.$contrat->getReference().'.pdf'];
            }
        }
        if (null !== $contrat->getPdfAudit()) {
            $auditPath = $projectDir.'/public/uploads/contrats/audit/'.$contrat->getPdfAudit();
            if (file_exists($auditPath)) {
                $attachments[] = ['path' => $auditPath, 'name' => 'audit-'.$contrat->getReference().'.pdf'];
            }
        }

        // Mail client
        try {
            $mailer->sendEmail(
                $contrat->getEmail(),
                'Contrat '.$contrat->getReference().' signe - '.$contrat->getTypeLabel(),
                $twig->render('emails/contrat_signed.html.twig', [
                    'contrat' => $contrat,
                ]),
                null,
                null,
                false,
                $attachments,
            );
        } catch (\Throwable) {
            // silencieux
        }

        // Mail admin
        try {
            $mailer->sendEmail(
                self::MONITOR_EMAIL,
                'Contrat '.$contrat->getReference().' signe par '.$contrat->getRaisonSociale(),
                $twig->render('emails/contrat_signed.html.twig', [
                    'contrat' => $contrat,
                ]),
                null,
                null,
                false,
                $attachments,
            );
        } catch (\Throwable) {
            // silencieux
        }

        return new JsonResponse(['status' => 'ok', 'event' => 'contrat_signed', 'reference' => $contrat->getReference()]);
    }

    /**
     * @param array<string, mixed> $data
     * @param array<string, mixed> $metadata
     */
    private function handleAttestationCustomEvent(
        string $eventType,
        array $data,
        array $metadata,
        EntityManagerInterface $em,
        string $projectDir,
    ): JsonResponse {
        if ('form.completed' !== $eventType) {
            return new JsonResponse(['status' => 'ok', 'event' => $eventType, 'doc_type' => 'attestation_custom']);
        }

        $attId = $metadata['attestation_custom_id'] ?? null;
        if (null === $attId) {
            return new JsonResponse(['status' => 'ignored', 'reason' => 'attestation_custom_id missing']);
        }

        $attestation = $em->getRepository(\App\Entity\AttestationCustom::class)->find((int) $attId);
        if (null === $attestation) {
            return new JsonResponse(['status' => 'ignored', 'reason' => 'attestation not found']);
        }

        // Telecharger le PDF signe
        $tmpFiles = [];
        $documents = $data['documents'] ?? [];
        $pdfUrl = $documents[0]['url'] ?? null;
        if (null !== $pdfUrl) {
            $content = @file_get_contents($pdfUrl);
            if (false !== $content && str_starts_with($content, '%PDF')) {
                $tmp = tempnam(sys_get_temp_dir(), 'att_c_signed_').'.pdf';
                file_put_contents($tmp, $content);
                $attestation->setPdfSignedFile(new \Symfony\Component\HttpFoundation\File\UploadedFile($tmp, 'attestation-signee-'.$attestation->getReference().'.pdf', 'application/pdf', null, true));
                $tmpFiles[] = $tmp;
            }
        }

        $auditUrl = $data['audit_log_url'] ?? null;
        if (null !== $auditUrl) {
            $auditContent = @file_get_contents($auditUrl);
            if (false !== $auditContent) {
                $tmp = tempnam(sys_get_temp_dir(), 'att_c_audit_').'.pdf';
                file_put_contents($tmp, $auditContent);
                $attestation->setPdfAuditFile(new \Symfony\Component\HttpFoundation\File\UploadedFile($tmp, 'audit-'.$attestation->getReference().'.pdf', 'application/pdf', null, true));
                $tmpFiles[] = $tmp;
            }
        }

        $attestation->setState(\App\Entity\AttestationCustom::STATE_SIGNED);
        $attestation->setSignedAt(new \DateTimeImmutable());
        $em->flush();

        foreach ($tmpFiles as $f) {
            @unlink($f);
        }

        return new JsonResponse(['status' => 'ok', 'event' => 'attestation_signed', 'reference' => $attestation->getReference()]);
    }

    /**
     * @param array<string, mixed> $data
     * @param array<string, mixed> $metadata
     */
    private function handleEFlexEvent(
        string $eventType,
        array $data,
        array $metadata,
        MailerService $mailer,
        Environment $twig,
        EntityManagerInterface $em,
        string $projectDir,
        string $stripeSk,
    ): JsonResponse {
        $eflexId = $metadata['eflex_id'] ?? null;
        $eflex = null !== $eflexId ? $em->getRepository(\App\Entity\EFlex::class)->find((int) $eflexId) : null;

        if (null === $eflex) {
            return new JsonResponse(['status' => 'ignored', 'reason' => 'eflex not found'], Response::HTTP_NOT_FOUND);
        }

        if ('form.completed' !== $eventType) {
            return new JsonResponse(['status' => 'ok', 'event' => $eventType, 'eflex' => $eflex->getReference()]);
        }

        // Telecharger le PDF signe et l'audit
        $tmpFiles = $this->downloadEFlexSignedDocuments($data, $eflex, $projectDir);
        $eflex->setState(\App\Entity\EFlex::STATE_PENDING_SETUP);
        $em->flush();

        foreach ($tmpFiles as $f) {
            @unlink($f);
        }

        // Preparer Stripe Customer
        $customer = $eflex->getCustomer();
        if ('' !== $stripeSk) {
            try {
                \Stripe\Stripe::setApiKey($stripeSk);
                $stripeCustomerId = $customer->getStripeCustomerId();
                if (null === $stripeCustomerId) {
                    $stripeCustomer = \Stripe\Customer::create([
                        'email' => $customer->getEmail(),
                        'name' => $customer->getFullName(),
                    ]);
                    $stripeCustomerId = $stripeCustomer->id;
                    $customer->setStripeCustomerId($stripeCustomerId);
                }
                $eflex->setStripeCustomerId($stripeCustomerId);
                $em->flush();
            } catch (\Throwable) {
                // Best effort
            }
        }

        // Envoyer le mail au client avec PDF signe + coordonnees bancaires + lien page paiement
        $ref = $eflex->getReference();
        $attachments = [];
        $tmpFiles = [];

        if (null !== $eflex->getPdfSigned()) {
            $signedPath = $projectDir.'/public/uploads/eflex/signed/'.$eflex->getPdfSigned();
            if (file_exists($signedPath)) {
                $attachments[] = ['path' => $signedPath, 'name' => 'eflex-signe-'.$ref.'.pdf'];
            }
        }
        if (null !== $eflex->getPdfAudit()) {
            $auditPath = $projectDir.'/public/uploads/eflex/audit/'.$eflex->getPdfAudit();
            if (file_exists($auditPath)) {
                $attachments[] = ['path' => $auditPath, 'name' => 'audit-'.$ref.'.pdf'];
            }
        }

        if (null !== $customer->getEmail()) {
            try {
                $processUrl = $this->generateUrl('app_eflex_verify', [
                    'id' => $eflex->getId(),
                ], UrlGeneratorInterface::ABSOLUTE_URL);

                $mailer->sendEmail(
                    $customer->getEmail(),
                    'E-Flex '.$ref.' signe - Configurez vos paiements',
                    $twig->render('emails/eflex_signed_client.html.twig', [
                        'customer' => $customer,
                        'eflex' => $eflex,
                        'processUrl' => $processUrl,
                    ]),
                    null,
                    null,
                    false,
                    $attachments,
                );
            } catch (\Throwable) {
                // silencieux
            }
        }

        // Mail admin
        try {
            $mailer->sendEmail(
                self::MONITOR_EMAIL,
                'E-Flex '.$ref.' signe par '.$customer->getFullName(),
                $twig->render('emails/eflex_signed_admin.html.twig', [
                    'customer' => $customer,
                    'eflex' => $eflex,
                ]),
                null,
                null,
                false,
                $attachments,
            );
        } catch (\Throwable) {
            // silencieux
        }

        return new JsonResponse(['status' => 'ok', 'event' => 'completed', 'eflex' => $ref]);
    }

    /**
     * @param array<string, mixed> $data
     *
     * @codeCoverageIgnore
     */
    /**
     * @return list<string> fichiers temporaires a supprimer apres flush
     */
    private function downloadEFlexSignedDocuments(array $data, \App\Entity\EFlex $eflex, string $projectDir): array
    {
        $tmpFiles = [];

        $documents = $data['documents'] ?? [];
        $pdfUrl = $documents[0]['url'] ?? null;
        if (null !== $pdfUrl) {
            $content = @file_get_contents($pdfUrl);
            if (false !== $content && str_starts_with($content, '%PDF')) {
                $tmpSigned = tempnam(sys_get_temp_dir(), 'eflex_signed_').'.pdf';
                file_put_contents($tmpSigned, $content);
                $eflex->setPdfSignedFile(new \Symfony\Component\HttpFoundation\File\UploadedFile($tmpSigned, 'eflex-signe-'.$eflex->getReference().'.pdf', 'application/pdf', null, true));
                $tmpFiles[] = $tmpSigned;
            }
        }

        $auditUrl = $data['audit_log_url'] ?? null;
        if (null !== $auditUrl) {
            $auditContent = @file_get_contents($auditUrl);
            if (false !== $auditContent) {
                $tmpAudit = tempnam(sys_get_temp_dir(), 'eflex_audit_').'.pdf';
                file_put_contents($tmpAudit, $auditContent);
                $eflex->setPdfAuditFile(new \Symfony\Component\HttpFoundation\File\UploadedFile($tmpAudit, 'audit-'.$eflex->getReference().'.pdf', 'application/pdf', null, true));
                $tmpFiles[] = $tmpAudit;
            }
        }

        return $tmpFiles;
    }

    /**
     * @param array<string, mixed> $data
     * @param array<string, mixed> $metadata
     */
    private function handleClientClosureEvent(
        string $eventType,
        array $data,
        array $metadata,
        MailerService $mailer,
        Environment $twig,
        EntityManagerInterface $em,
    ): JsonResponse {
        if ('form.completed' !== $eventType) {
            return new JsonResponse(['status' => 'ok', 'event' => $eventType, 'doc_type' => 'client_closure']);
        }

        $customerId = $metadata['customer_id'] ?? null;
        if (null === $customerId) {
            return new JsonResponse(['status' => 'ignored', 'reason' => 'customer_id missing'], Response::HTTP_NOT_FOUND);
        }

        $customer = $em->getRepository(\App\Entity\Customer::class)->find((int) $customerId);
        if (null === $customer || null === $customer->getEmail()) {
            return new JsonResponse(['status' => 'ignored', 'reason' => 'customer not found']);
        }

        // Telecharger le PDF signe
        $attachments = [];
        $tmpFiles = [];

        $documents = $data['documents'] ?? [];
        $pdfUrl = $documents[0]['url'] ?? null;
        if (null !== $pdfUrl) {
            $content = @file_get_contents($pdfUrl);
            if (false !== $content && str_starts_with($content, '%PDF')) {
                $tmp = tempnam(sys_get_temp_dir(), 'closure_signed_').'.pdf';
                file_put_contents($tmp, $content);
                $attachments[] = ['path' => $tmp, 'name' => 'notification-cloture-signee.pdf'];
                $tmpFiles[] = $tmp;
            }
        }

        $auditUrl = $data['audit_log_url'] ?? null;
        if (null !== $auditUrl) {
            $auditContent = @file_get_contents($auditUrl);
            if (false !== $auditContent) {
                $tmp = tempnam(sys_get_temp_dir(), 'closure_audit_').'.pdf';
                file_put_contents($tmp, $auditContent);
                $attachments[] = ['path' => $tmp, 'name' => 'audit-cloture.pdf'];
                $tmpFiles[] = $tmp;
            }
        }

        // Mail au client
        try {
            $mailer->sendEmail(
                $customer->getEmail(),
                'Notification de cloture definitive de votre compte - Association E-Cosplay',
                $twig->render('emails/client_closure.html.twig', [
                    'customer' => $customer,
                ]),
                null,
                null,
                false,
                $attachments,
            );
        } catch (\Throwable) {
            // silencieux
        }

        // Mail admin + direction
        foreach ([self::MONITOR_EMAIL, 'direction@e-cosplay.fr'] as $adminEmail) {
            try {
                $mailer->sendEmail(
                    $adminEmail,
                    'Cloture compte '.$customer->getFullName().' - Notification envoyee',
                    $twig->render('emails/client_closure.html.twig', [
                        'customer' => $customer,
                    ]),
                    null,
                    null,
                    false,
                    $attachments,
                );
            } catch (\Throwable) {
                // silencieux
            }
        }

        foreach ($tmpFiles as $tmp) {
            @unlink($tmp);
        }

        return new JsonResponse(['status' => 'ok', 'event' => 'closure_sent', 'customer' => $customer->getFullName()]);
    }

    /**
     * @param array<string, mixed> $data
     * @param array<string, mixed> $metadata
     */
    private function handleClientWarningEvent(
        string $eventType,
        array $data,
        array $metadata,
        MailerService $mailer,
        Environment $twig,
        EntityManagerInterface $em,
    ): JsonResponse {
        if ('form.completed' !== $eventType) {
            return new JsonResponse(['status' => 'ok', 'event' => $eventType, 'doc_type' => 'client_warning']);
        }

        $customerId = $metadata['customer_id'] ?? null;
        if (null === $customerId) {
            return new JsonResponse(['status' => 'ignored', 'reason' => 'customer_id missing'], Response::HTTP_NOT_FOUND);
        }

        $customer = $em->getRepository(\App\Entity\Customer::class)->find((int) $customerId);
        if (null === $customer || null === $customer->getEmail()) {
            return new JsonResponse(['status' => 'ignored', 'reason' => 'customer not found']);
        }

        $level = $metadata['level'] ?? '1st';
        $reasons = isset($metadata['reasons']) && '' !== $metadata['reasons'] ? explode(',', $metadata['reasons']) : [];

        $warningLabels = [
            '1st' => '1er avertissement',
            '2nd' => '2eme avertissement',
            'last' => 'Dernier avertissement avant suspension',
        ];

        // Telecharger le PDF signe
        $attachments = [];
        $tmpFiles = [];

        $documents = $data['documents'] ?? [];
        $pdfUrl = $documents[0]['url'] ?? null;
        if (null !== $pdfUrl) {
            $content = @file_get_contents($pdfUrl);
            if (false !== $content && str_starts_with($content, '%PDF')) {
                $tmp = tempnam(sys_get_temp_dir(), 'warn_signed_').'.pdf';
                file_put_contents($tmp, $content);
                $attachments[] = ['path' => $tmp, 'name' => 'avertissement-signe-'.$level.'.pdf'];
                $tmpFiles[] = $tmp;
            }
        }

        $auditUrl = $data['audit_log_url'] ?? null;
        if (null !== $auditUrl) {
            $auditContent = @file_get_contents($auditUrl);
            if (false !== $auditContent) {
                $tmp = tempnam(sys_get_temp_dir(), 'warn_audit_').'.pdf';
                file_put_contents($tmp, $auditContent);
                $attachments[] = ['path' => $tmp, 'name' => 'audit-avertissement.pdf'];
                $tmpFiles[] = $tmp;
            }
        }

        // Mail au client avec PDF signe
        try {
            $mailer->sendEmail(
                $customer->getEmail(),
                ($warningLabels[$level] ?? 'Avertissement').' - Association E-Cosplay',
                $twig->render('emails/client_warning.html.twig', [
                    'customer' => $customer,
                    'level' => $level,
                    'warningLabel' => $warningLabels[$level] ?? 'Avertissement',
                    'reasons' => $reasons,
                ]),
                null,
                null,
                false,
                $attachments,
            );
        } catch (\Throwable) {
            // silencieux
        }

        // Mail admin
        try {
            $mailer->sendEmail(
                self::MONITOR_EMAIL,
                'Avertissement '.$level.' envoye a '.$customer->getFullName(),
                $twig->render('emails/client_warning.html.twig', [
                    'customer' => $customer,
                    'level' => $level,
                    'warningLabel' => $warningLabels[$level] ?? 'Avertissement',
                    'reasons' => $reasons,
                ]),
                null,
                null,
                false,
                $attachments,
            );
        } catch (\Throwable) {
            // silencieux
        }

        foreach ($tmpFiles as $tmp) {
            @unlink($tmp);
        }

        return new JsonResponse(['status' => 'ok', 'event' => 'warning_sent', 'customer' => $customer->getFullName(), 'level' => $level]);
    }

    /**
     * @param array<string, mixed> $data
     * @param array<string, mixed> $metadata
     */
    private function handleClientWarningResetEvent(
        string $eventType,
        array $data,
        array $metadata,
        MailerService $mailer,
        Environment $twig,
        EntityManagerInterface $em,
    ): JsonResponse {
        if ('form.completed' !== $eventType) {
            return new JsonResponse(['status' => 'ok', 'event' => $eventType, 'doc_type' => 'client_warning_reset']);
        }

        $customerId = $metadata['customer_id'] ?? null;
        if (null === $customerId) {
            return new JsonResponse(['status' => 'ignored', 'reason' => 'customer_id missing'], Response::HTTP_NOT_FOUND);
        }

        $customer = $em->getRepository(\App\Entity\Customer::class)->find((int) $customerId);
        if (null === $customer || null === $customer->getEmail()) {
            return new JsonResponse(['status' => 'ignored', 'reason' => 'customer not found']);
        }

        // Telecharger le PDF signe
        $attachments = [];
        $tmpFiles = [];

        $documents = $data['documents'] ?? [];
        $pdfUrl = $documents[0]['url'] ?? null;
        if (null !== $pdfUrl) {
            $content = @file_get_contents($pdfUrl);
            if (false !== $content && str_starts_with($content, '%PDF')) {
                $tmp = tempnam(sys_get_temp_dir(), 'reset_signed_').'.pdf';
                file_put_contents($tmp, $content);
                $attachments[] = ['path' => $tmp, 'name' => 'levee-avertissement-signe.pdf'];
                $tmpFiles[] = $tmp;
            }
        }

        // Mail au client
        try {
            $mailer->sendEmail(
                $customer->getEmail(),
                'Regularisation de votre situation - Association E-Cosplay',
                $twig->render('emails/client_warning_reset.html.twig', [
                    'customer' => $customer,
                ]),
                null,
                null,
                false,
                $attachments,
            );
        } catch (\Throwable) {
            // silencieux
        }

        foreach ($tmpFiles as $tmp) {
            @unlink($tmp);
        }

        return new JsonResponse(['status' => 'ok', 'event' => 'warning_reset_sent', 'customer' => $customer->getFullName()]);
    }

    /**
     * @param array<string, mixed> $data
     * @param array<string, mixed> $metadata
     */
    private function handleEcheancierAttestationEvent(
        string $eventType,
        array $data,
        array $metadata,
        MailerService $mailer,
        Environment $twig,
        EntityManagerInterface $em,
        string $projectDir,
    ): JsonResponse {
        if ('form.completed' !== $eventType) {
            return new JsonResponse(['status' => 'ok', 'event' => $eventType, 'doc_type' => 'echeancier_attestation']);
        }

        $echeancier = $this->resolveEcheancier($data, $metadata, $em);
        if (null === $echeancier) {
            return new JsonResponse(['status' => 'ignored', 'reason' => 'echeancier not found'], Response::HTTP_NOT_FOUND);
        }

        $customer = $echeancier->getCustomer();
        $ref = $echeancier->getReference();

        // Telecharger le PDF signe depuis DocuSeal
        $attachments = [];
        $tmpFiles = [];

        $documents = $data['documents'] ?? [];
        $pdfUrl = $documents[0]['url'] ?? null;
        if (null !== $pdfUrl) {
            $content = @file_get_contents($pdfUrl);
            if (false !== $content && str_starts_with($content, '%PDF')) {
                $tmpSigned = tempnam(sys_get_temp_dir(), 'att_signed_').'.pdf';
                file_put_contents($tmpSigned, $content);
                $attachments[] = ['path' => $tmpSigned, 'name' => 'attestation-signee-'.$ref.'.pdf'];
                $tmpFiles[] = $tmpSigned;
            }
        }

        $auditUrl = $data['audit_log_url'] ?? null;
        if (null !== $auditUrl) {
            $auditContent = @file_get_contents($auditUrl);
            if (false !== $auditContent) {
                $tmpAudit = tempnam(sys_get_temp_dir(), 'att_audit_').'.pdf';
                file_put_contents($tmpAudit, $auditContent);
                $attachments[] = ['path' => $tmpAudit, 'name' => 'audit-attestation-'.$ref.'.pdf'];
                $tmpFiles[] = $tmpAudit;
            }
        }

        $remaining = $echeancier->getTotalWithMajoration() - $echeancier->getTotalPaid();

        // Mail client avec PDF signe en piece jointe
        if (null !== $customer->getEmail()) {
            try {
                $mailer->sendEmail(
                    $customer->getEmail(),
                    'Attestation echeancier '.$ref,
                    $twig->render('emails/echeancier_attestation.html.twig', [
                        'customer' => $customer,
                        'echeancier' => $echeancier,
                        'remaining' => $remaining,
                    ]),
                    null,
                    null,
                    false,
                    $attachments,
                );
            } catch (\Throwable) {
                // silencieux
            }
        }

        // Mail admin
        try {
            $mailer->sendEmail(
                self::MONITOR_EMAIL,
                'Attestation '.$ref.' signee - '.$customer->getFullName(),
                $twig->render('emails/echeancier_attestation.html.twig', [
                    'customer' => $customer,
                    'echeancier' => $echeancier,
                    'remaining' => $remaining,
                ]),
                null,
                null,
                false,
                $attachments,
            );
        } catch (\Throwable) {
            // silencieux
        }

        // Nettoyage fichiers temporaires
        foreach ($tmpFiles as $tmp) {
            @unlink($tmp);
        }

        return new JsonResponse(['status' => 'ok', 'event' => 'attestation_sent', 'echeancier' => $ref]);
    }

    /**
     * @param array<string, mixed> $data
     * @param array<string, mixed> $metadata
     */
    private function handleEcheancierEvent(
        string $eventType,
        array $data,
        array $metadata,
        DocuSealService $docuSealService,
        MailerService $mailer,
        Environment $twig,
        EntityManagerInterface $em,
        string $projectDir,
        string $stripeSk,
    ): JsonResponse {
        $echeancier = $this->resolveEcheancier($data, $metadata, $em);

        if (null === $echeancier) {
            return new JsonResponse(['status' => 'ignored', 'reason' => 'echeancier not found'], Response::HTTP_NOT_FOUND);
        }

        return match ($eventType) {
            'form.completed' => $this->handleEcheancierCompleted($echeancier, $docuSealService, $mailer, $twig, $em, $projectDir, $stripeSk),
            'form.declined' => $this->handleEcheancierDeclined($echeancier, $data, $mailer, $twig, $em),
            default => new JsonResponse(['status' => 'ok', 'event' => $eventType, 'echeancier' => $echeancier->getReference()]),
        };
    }

    /**
     * @param array<string, mixed> $data
     * @param array<string, mixed> $metadata
     */
    private function resolveEcheancier(array $data, array $metadata, EntityManagerInterface $em): ?Echeancier
    {
        $echeancierId = $metadata['echeancier_id'] ?? null;
        if (null !== $echeancierId) {
            $echeancier = $em->getRepository(Echeancier::class)->find((int) $echeancierId);
            if (null !== $echeancier) {
                return $echeancier;
            }
        }

        $submitterId = $data['id'] ?? null;
        if (null !== $submitterId) {
            return $em->getRepository(Echeancier::class)->findOneBy(['submissionId' => (string) $submitterId]);
        }

        return null;
    }

    private function handleEcheancierCompleted(
        Echeancier $echeancier,
        DocuSealService $docuSealService,
        MailerService $mailer,
        Environment $twig,
        EntityManagerInterface $em,
        string $projectDir,
        string $stripeSk,
    ): JsonResponse {
        $echeancier->setState(Echeancier::STATE_SIGNED);
        $em->flush();

        // Telecharger le PDF signe et l'audit depuis DocuSeal
        $docuSealService->downloadSignedEcheancier($echeancier);

        // Preparer le client Stripe et envoyer le lien SEPA
        $this->prepareSepaForEcheancier($echeancier, $stripeSk, $mailer, $twig, $em);

        // Notifications email : admin + client
        $this->sendEcheancierSignedNotifications($echeancier, $mailer, $twig, $projectDir);

        return new JsonResponse(['status' => 'ok', 'event' => 'completed', 'echeancier' => $echeancier->getReference()]);
    }

    /**
     * Prepare le client Stripe et envoie le lien de configuration SEPA au client.
     *
     * @codeCoverageIgnore Interaction Stripe API
     */
    private function prepareSepaForEcheancier(
        Echeancier $echeancier,
        string $stripeSk,
        MailerService $mailer,
        Environment $twig,
        EntityManagerInterface $em,
    ): void {
        if ('' === $stripeSk) {
            return;
        }

        $customer = $echeancier->getCustomer();

        try {
            \Stripe\Stripe::setApiKey($stripeSk);

            $stripeCustomerId = $customer->getStripeCustomerId();
            if (null === $stripeCustomerId) {
                $stripeCustomer = \Stripe\Customer::create([
                    'email' => $customer->getEmail(),
                    'name' => $customer->getFullName(),
                ]);
                $stripeCustomerId = $stripeCustomer->id;
                $customer->setStripeCustomerId($stripeCustomerId);
            }

            $echeancier->setStripeCustomerId($stripeCustomerId);
            $echeancier->setState(Echeancier::STATE_PENDING_SETUP);
            $em->flush();

            // Envoyer le lien de configuration SEPA au client
            if (null !== $customer->getEmail()) {
                $setupUrl = $this->generateUrl('app_echeancier_setup_payment', [
                    'id' => $echeancier->getId(),
                ], UrlGeneratorInterface::ABSOLUTE_URL);

                $mailer->sendEmail(
                    $customer->getEmail(),
                    'Configurez votre prelevement SEPA - Echeancier '.$echeancier->getReference(),
                    $twig->render('emails/echeancier_stripe_setup.html.twig', [
                        'customer' => $customer,
                        'echeancier' => $echeancier,
                        'setupUrl' => $setupUrl,
                    ]),
                    null,
                    null,
                    false,
                );
            }
        } catch (\Throwable) {
            // Best effort
        }
    }

    /**
     * @param array<string, mixed> $data
     */
    private function handleEcheancierDeclined(
        Echeancier $echeancier,
        array $data,
        MailerService $mailer,
        Environment $twig,
        EntityManagerInterface $em,
    ): JsonResponse {
        $echeancier->setState(Echeancier::STATE_CANCELLED);
        $em->flush();

        $reason = isset($data['decline_reason']) && '' !== $data['decline_reason']
            ? (string) $data['decline_reason']
            : null;

        // Notifications email : admin + client
        $this->sendEcheancierRefusedNotifications($echeancier, $reason, $mailer, $twig);

        return new JsonResponse(['status' => 'ok', 'event' => 'declined', 'echeancier' => $echeancier->getReference()]);
    }

    private function sendEcheancierSignedNotifications(Echeancier $echeancier, MailerService $mailer, Environment $twig, string $projectDir): void
    {
        $customer = $echeancier->getCustomer();
        $ref = $echeancier->getReference();

        $attachments = [];

        if (null !== $echeancier->getPdfSigned()) {
            $signedPath = $projectDir.'/public/uploads/echeanciers/signed/'.$echeancier->getPdfSigned();
            if (file_exists($signedPath)) {
                $attachments[] = ['path' => $signedPath, 'name' => 'echeancier-signe-'.$ref.'.pdf'];
            }
        }
        if (null !== $echeancier->getPdfAudit()) {
            $auditPath = $projectDir.'/public/uploads/echeanciers/audit/'.$echeancier->getPdfAudit();
            if (file_exists($auditPath)) {
                $attachments[] = ['path' => $auditPath, 'name' => 'audit-'.$ref.'.pdf'];
            }
        }

        // Mail client
        if (null !== $customer->getEmail()) {
            try {
                $mailer->sendEmail(
                    $customer->getEmail(),
                    'Echeancier '.$ref.' signe avec succes',
                    $twig->render('emails/echeancier_signed_client.html.twig', [
                        'customer' => $customer,
                        'echeancier' => $echeancier,
                    ]),
                    null,
                    null,
                    false,
                    $attachments,
                );
            } catch (\Throwable) {
                // silencieux
            }
        }

        // Mail admin
        try {
            $mailer->sendEmail(
                self::MONITOR_EMAIL,
                'Echeancier '.$ref.' signe par '.$customer->getFullName(),
                $twig->render('emails/echeancier_signed_admin.html.twig', [
                    'customer' => $customer,
                    'echeancier' => $echeancier,
                ]),
                null,
                null,
                false,
                $attachments,
            );
        } catch (\Throwable) {
            // silencieux
        }
    }

    private function sendEcheancierRefusedNotifications(Echeancier $echeancier, ?string $reason, MailerService $mailer, Environment $twig): void
    {
        $customer = $echeancier->getCustomer();
        $ref = $echeancier->getReference();

        // Mail client
        if (null !== $customer->getEmail()) {
            try {
                $mailer->sendEmail(
                    $customer->getEmail(),
                    'Echeancier '.$ref.' refuse',
                    $twig->render('emails/echeancier_refused_client.html.twig', [
                        'customer' => $customer,
                        'echeancier' => $echeancier,
                        'reason' => $reason,
                    ]),
                    null,
                    null,
                    false,
                );
            } catch (\Throwable) {
                // silencieux
            }
        }

        // Mail admin
        try {
            $mailer->sendEmail(
                self::MONITOR_EMAIL,
                'Echeancier '.$ref.' refuse par '.$customer->getFullName(),
                $twig->render('emails/echeancier_refused_admin.html.twig', [
                    'customer' => $customer,
                    'echeancier' => $echeancier,
                    'reason' => $reason,
                ]),
                null,
                null,
                false,
            );
        } catch (\Throwable) {
            // silencieux
        }
    }

    /**
     * Envoie 2 emails : "Vous avez signe votre devis" au client + notification admin avec PDFs en piece jointe.
     */
    private function sendDevisSignedNotifications(Devis $devis, MailerService $mailer, Environment $twig, string $projectDir): void
    {
        $customer = $devis->getCustomer();
        if (null === $customer || null === $customer->getEmail()) {
            return;
        }

        $numOrder = $devis->getOrderNumber()->getNumOrder();

        // Mail client : confirmation signature
        try {
            $mailer->sendEmail(
                $customer->getEmail(),
                'Vous avez signe votre devis '.$numOrder,
                $twig->render('emails/devis_signed_client.html.twig', [
                    'customer' => $customer,
                    'devis' => $devis,
                ]),
            );
        } catch (\Throwable) {
            // silencieux
        }

        // Mail admin : notification avec PDFs en piece jointe
        $attachments = [];
        $uploadDir = $projectDir.'/public/uploads/devis';

        if (null !== $devis->getSignedPdf()) {
            $signedPath = $uploadDir.'/'.$devis->getSignedPdf();
            if (file_exists($signedPath)) {
                $attachments[] = ['path' => $signedPath, 'name' => 'devis-signe-'.str_replace('/', '-', $numOrder).'.pdf'];
            }
        }
        if (null !== $devis->getAuditPdf()) {
            $auditPath = $uploadDir.'/'.$devis->getAuditPdf();
            if (file_exists($auditPath)) {
                $attachments[] = ['path' => $auditPath, 'name' => 'audit-'.str_replace('/', '-', $numOrder).'.pdf'];
            }
        }

        try {
            $mailer->sendEmail(
                self::MONITOR_EMAIL,
                'Devis '.$numOrder.' signe par '.$customer->getFullName(),
                $twig->render('emails/devis_signed_admin.html.twig', [
                    'customer' => $customer,
                    'devis' => $devis,
                ]),
                null,
                null,
                false,
                $attachments,
            );
        } catch (\Throwable) {
            // silencieux
        }
    }

    /**
     * Envoie 2 emails : "Vous avez refuse votre devis" au client + notification admin du refus.
     */
    private function sendDevisRefusedNotifications(Devis $devis, ?string $reason, MailerService $mailer, Environment $twig): void
    {
        $customer = $devis->getCustomer();
        if (null === $customer || null === $customer->getEmail()) {
            return;
        }

        $numOrder = $devis->getOrderNumber()->getNumOrder();

        try {
            $mailer->sendEmail(
                $customer->getEmail(),
                'Vous avez refuse le devis '.$numOrder,
                $twig->render('emails/devis_refused_client.html.twig', [
                    'customer' => $customer,
                    'devis' => $devis,
                    'reason' => $reason,
                ]),
            );
        } catch (\Throwable) {
            // silencieux
        }

        try {
            $mailer->sendEmail(
                self::MONITOR_EMAIL,
                'Devis '.$numOrder.' refuse par '.$customer->getFullName(),
                $twig->render('emails/devis_refused_admin.html.twig', [
                    'customer' => $customer,
                    'devis' => $devis,
                    'reason' => $reason,
                ]),
                null,
                null,
                false,
            );
        } catch (\Throwable) {
            // silencieux
        }
    }

    /**
     * @return array<string, mixed>|Response
     */
    private function parseAndValidate(Request $request, string $secretHeader, string $secret): array|Response
    {
        if (!$this->verifySecret($request, $secretHeader, $secret)) {
            return new JsonResponse(['error' => 'Unauthorized'], Response::HTTP_UNAUTHORIZED);
        }

        $payload = json_decode($request->getContent(), true);

        if (null === $payload) {
            return new JsonResponse(['error' => 'Invalid payload'], Response::HTTP_BAD_REQUEST);
        }

        return $payload;
    }

    private function verifySecret(Request $request, string $secretHeader, string $secret): bool
    {
        if ('' === $secret) {
            return true;
        }

        $headerValue = $request->headers->get($secretHeader, '');
        if (hash_equals($secret, $headerValue)) {
            return true;
        }

        $body = json_decode($request->getContent(), true);
        $bodySecret = $body['secret'] ?? $body['webhook_secret'] ?? '';

        return '' !== $bodySecret && hash_equals($secret, $bodySecret);
    }

    /** @param array<string, mixed> $metadata */
    private function syncSubmitterIdFromMetadata(?string $docType, mixed $submitterId, array $metadata, AttestationRepository $attestationRepository, EntityManagerInterface $em): void
    {
        if ('attestation' !== $docType || null === $submitterId || null !== $this->findAttestation($submitterId, $attestationRepository)) {
            return;
        }

        $reference = $metadata['reference'] ?? null;
        if (null === $reference) {
            return;
        }

        $attestation = $attestationRepository->findOneBy(['reference' => $reference]);
        if (null !== $attestation) {
            $attestation->setSubmitterId((int) $submitterId);
            $em->flush();
        }
    }

    private function findAttestation(?int $submitterId, AttestationRepository $repository): ?Attestation
    {
        if (null === $submitterId) {
            return null;
        }

        return $repository->findOneBy(['submitterId' => $submitterId]);
    }

    private function handleAttestationViewed(?int $submitterId, AttestationRepository $attestationRepository): JsonResponse
    {
        $attestation = $this->findAttestation($submitterId, $attestationRepository);

        if (null === $attestation) {
            return new JsonResponse(['error' => self::ATTESTATION_NOT_FOUND], Response::HTTP_NOT_FOUND);
        }

        return new JsonResponse(['status' => 'ok', 'event' => 'viewed', 'reference' => $attestation->getReference()]);
    }

    private function handleAttestationStarted(?int $submitterId, AttestationRepository $attestationRepository): JsonResponse
    {
        $attestation = $this->findAttestation($submitterId, $attestationRepository);

        if (null === $attestation) {
            return new JsonResponse(['error' => self::ATTESTATION_NOT_FOUND], Response::HTTP_NOT_FOUND);
        }

        return new JsonResponse(['status' => 'ok', 'event' => 'started', 'reference' => $attestation->getReference()]);
    }

    /**
     * @param array<string, mixed> $data
     */
    private function handleAttestationCompleted(
        array $data,
        AttestationRepository $attestationRepository,
        MailerService $mailer,
        EntityManagerInterface $em,
        Environment $twig,
        string $projectDir,
    ): JsonResponse {
        $submitterId = $data['id'] ?? null;
        $attestation = $this->findAttestation($submitterId, $attestationRepository);

        if (null === $attestation) {
            return new JsonResponse(['error' => self::ATTESTATION_NOT_FOUND], Response::HTTP_NOT_FOUND);
        }

        $attestation->markAsSigned();
        $this->downloadDocumentsFromWebhook($data, $attestation, $projectDir);

        $signedPdf = $attestation->getPdfFileSigned();
        $certificatePdf = $attestation->getPdfFileCertificate();

        $attachments = [];
        if (null !== $signedPdf && file_exists($signedPdf)) {
            $attachments[] = ['path' => $signedPdf, 'name' => 'attestation-signee-'.$attestation->getReference().'.pdf'];
        }
        if (null !== $certificatePdf && file_exists($certificatePdf)) {
            $attachments[] = ['path' => $certificatePdf, 'name' => 'certificat-'.$attestation->getReference().'.pdf'];
        }

        $typeName = match ($attestation->getType()) {
            'access' => 'acces aux donnees',
            'deletion' => 'suppression des donnees',
            'no_data' => 'absence de donnees',
            default => 'RGPD',
        };

        $mailer->sendEmail(
            $attestation->getEmail(),
            'CRM E-Cosplay - Attestation signee '.$typeName.' ('.$attestation->getReference().')',
            $twig->render('emails/rgpd_attestation_signed.html.twig', [
                'attestation' => $attestation,
                'typeName' => $typeName,
            ]),
            null,
            null,
            false,
            $attachments,
        );

        $attestation->markAsSent();
        $em->flush();

        return new JsonResponse(['status' => 'ok', 'event' => 'completed', 'reference' => $attestation->getReference()]);
    }

    /**
     * @param array<string, mixed> $data
     *
     * @codeCoverageIgnore Telecharge les PDFs depuis des URLs externes (non testable unitairement)
     */
    private function downloadDocumentsFromWebhook(array $data, Attestation $attestation, string $projectDir): void
    {
        $dir = $projectDir.'/var/rgpd/signed';
        if (!is_dir($dir)) {
            mkdir($dir, 0775, true);
        }

        $ref = $attestation->getReference();

        $documents = $data['documents'] ?? [];
        $pdfUrl = $documents[0]['url'] ?? null;
        if (null !== $pdfUrl) {
            $content = @file_get_contents($pdfUrl);
            if (false !== $content && str_starts_with($content, '%PDF')) {
                $path = $dir.'/'.$ref.'-signed.pdf';
                file_put_contents($path, $content);
                $attestation->setPdfFileSigned($path);
            }
        }

        $auditUrl = $data['audit_log_url'] ?? null;
        if (null !== $auditUrl) {
            $content = @file_get_contents($auditUrl);
            if (false !== $content) {
                $path = $dir.'/'.$ref.'-certificat.pdf';
                file_put_contents($path, $content);
                $attestation->setPdfFileCertificate($path);
            }
        }
    }

    /**
     * @param array<string, mixed> $data
     */
    private function handleAttestationDeclined(
        array $data,
        AttestationRepository $attestationRepository,
        EntityManagerInterface $em,
    ): JsonResponse {
        $submitterId = $data['id'] ?? null;
        $attestation = $this->findAttestation($submitterId, $attestationRepository);

        if (null === $attestation) {
            return new JsonResponse(['error' => self::ATTESTATION_NOT_FOUND], Response::HTTP_NOT_FOUND);
        }

        if ('signed' !== $attestation->getStatus()) {
            $em->flush();
        }

        return new JsonResponse(['status' => 'ok', 'event' => 'declined', 'reference' => $attestation->getReference()]);
    }
}
