<?php

namespace App\Tests\Controller\Admin;

use App\Controller\Admin\ComptabiliteController;
use App\Entity\User;
use App\Service\ComptaExportService;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Twig\Environment;

class ComptabiliteControllerTest extends TestCase
{
    private function buildEmWithQueryBuilder(): EntityManagerInterface
    {
        $stubEm = $this->createStub(EntityManagerInterface::class);

        $query = $this->getMockBuilder(Query::class)
            ->setConstructorArgs([$stubEm])
            ->onlyMethods(['getResult', '_doExecute', 'getSQL'])
            ->getMock();
        $query->method('getResult')->willReturn([]);

        $qb = $this->createStub(QueryBuilder::class);
        $qb->method('select')->willReturnSelf();
        $qb->method('from')->willReturnSelf();
        $qb->method('where')->willReturnSelf();
        $qb->method('andWhere')->willReturnSelf();
        $qb->method('setParameter')->willReturnSelf();
        $qb->method('orderBy')->willReturnSelf();
        $qb->method('getQuery')->willReturn($query);

        $em = $this->createStub(EntityManagerInterface::class);
        $em->method('createQueryBuilder')->willReturn($qb);

        return $em;
    }

    private function buildKernel(): KernelInterface
    {
        $tmpDir = sys_get_temp_dir().'/comptabilite_test_'.uniqid();
        mkdir($tmpDir.'/public', 0777, true);
        // logo.jpg is optional — ComptaPdf checks file_exists before using it

        $kernel = $this->createStub(KernelInterface::class);
        $kernel->method('getProjectDir')->willReturn($tmpDir);

        return $kernel;
    }

    private function buildHelper(?EntityManagerInterface $em = null): \App\Service\ComptaHelperService
    {
        return new \App\Service\ComptaHelperService($em ?? $this->buildEmWithQueryBuilder());
    }

    private function buildExportService(?EntityManagerInterface $em = null): ComptaExportService
    {
        $emInstance = $em ?? $this->buildEmWithQueryBuilder();

        return new ComptaExportService($emInstance, false, new \App\Service\ComptaHelperService($emInstance));
    }

    private function buildExportServiceWithTva(?EntityManagerInterface $em = null): ComptaExportService
    {
        $emInstance = $em ?? $this->buildEmWithQueryBuilder();

        return new ComptaExportService($emInstance, true, new \App\Service\ComptaHelperService($emInstance));
    }

    /**
     * Build a controller wired with a user token and a router that returns non-empty paths.
     * Required for methods that call getUser() and generateUrl()/redirectToRoute().
     */
    private function buildSignController(?ComptaExportService $exportService = null): \App\Controller\Admin\ComptabiliteController
    {
        $em = $this->buildEmWithQueryBuilder();
        $kernel = $this->buildKernel();
        $svc = $exportService ?? $this->buildExportService($em);
        $controller = new \App\Controller\Admin\ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc, $this->buildHelper($em));

        $session = new Session(new MockArraySessionStorage());
        $stack = $this->createStub(RequestStack::class);
        $stack->method('getSession')->willReturn($session);

        $twig = $this->createStub(\Twig\Environment::class);
        $twig->method('render')->willReturn('<html></html>');

        $router = $this->createStub(\Symfony\Component\Routing\RouterInterface::class);
        $router->method('generate')->willReturn('/admin/comptabilite');

        $user = new User();
        $user->setEmail('admin@e-cosplay.fr');
        $user->setFirstName('Admin');
        $user->setLastName('Test');
        $user->setPassword('h');
        $token = $this->createStub(TokenInterface::class);
        $token->method('getUser')->willReturn($user);
        $tokenStorage = $this->createStub(TokenStorageInterface::class);
        $tokenStorage->method('getToken')->willReturn($token);

        $container = $this->createStub(ContainerInterface::class);
        $container->method('has')->willReturnMap([
            ['twig', true],
            ['router', true],
            ['security.authorization_checker', true],
            ['security.token_storage', true],
            ['request_stack', true],
            ['parameter_bag', true],
            ['serializer', false],
        ]);
        $container->method('get')->willReturnMap([
            ['twig', $twig],
            ['router', $router],
            ['security.authorization_checker', $this->createStub(AuthorizationCheckerInterface::class)],
            ['security.token_storage', $tokenStorage],
            ['request_stack', $stack],
            ['parameter_bag', $this->createStub(ParameterBagInterface::class)],
        ]);
        $controller->setContainer($container);

        return $controller;
    }

    private function buildController(?ComptaExportService $exportService = null): ComptabiliteController
    {
        $em = $this->buildEmWithQueryBuilder();
        $kernel = $this->buildKernel();
        $svc = $exportService ?? $this->buildExportService($em);

        $controller = new ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc, $this->buildHelper($em));

        $session = new Session(new MockArraySessionStorage());
        $stack = $this->createStub(RequestStack::class);
        $stack->method('getSession')->willReturn($session);

        $twig = $this->createStub(Environment::class);
        $twig->method('render')->willReturn('<html></html>');

        $container = $this->createStub(ContainerInterface::class);
        $container->method('has')->willReturnMap([
            ['twig', true],
            ['router', true],
            ['security.authorization_checker', true],
            ['security.token_storage', true],
            ['request_stack', true],
            ['parameter_bag', true],
            ['serializer', false],
        ]);
        $container->method('get')->willReturnMap([
            ['twig', $twig],
            ['router', $this->createStub(RouterInterface::class)],
            ['security.authorization_checker', $this->createStub(AuthorizationCheckerInterface::class)],
            ['security.token_storage', $this->createStub(TokenStorageInterface::class)],
            ['request_stack', $stack],
            ['parameter_bag', $this->createStub(ParameterBagInterface::class)],
        ]);
        $controller->setContainer($container);

        return $controller;
    }

    public function testIndexReturns200(): void
    {
        $controller = $this->buildController();
        $response = $controller->index();
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportJournalVentesCsv(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current', 'format' => 'csv']);
        $response = $controller->exportJournalVentes($request);
        $this->assertSame(200, $response->getStatusCode());
        $contentType = $response->headers->get('Content-Type') ?? '';
        $this->assertStringContainsString('text/csv', $contentType);
    }

    public function testExportJournalVentesJson(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportJournalVentes($request);
        $this->assertSame(200, $response->getStatusCode());
        $contentType = $response->headers->get('Content-Type') ?? '';
        $this->assertStringContainsString('application/json', $contentType);
    }

    public function testExportJournalVentesPreviousPeriod(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'previous', 'format' => 'csv']);
        $response = $controller->exportJournalVentes($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportJournalVentesCustomPeriod(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'custom', 'from' => '2026-01-01', 'to' => '2026-03-31', 'format' => 'csv']);
        $response = $controller->exportJournalVentes($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportCommissionsStripeCsv(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current', 'format' => 'csv']);
        $response = $controller->exportCommissionsStripe($request);
        $this->assertSame(200, $response->getStatusCode());
        $contentType = $response->headers->get('Content-Type') ?? '';
        $this->assertStringContainsString('text/csv', $contentType);
    }

    public function testExportCommissionsStripeJson(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportCommissionsStripe($request);
        $this->assertSame(200, $response->getStatusCode());
        $contentType = $response->headers->get('Content-Type') ?? '';
        $this->assertStringContainsString('application/json', $contentType);
    }

    public function testExportCoutsServicesCsv(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current', 'format' => 'csv']);
        $response = $controller->exportCoutsServices($request);
        $this->assertSame(200, $response->getStatusCode());
        $contentType = $response->headers->get('Content-Type') ?? '';
        $this->assertStringContainsString('text/csv', $contentType);
    }

    public function testExportPdfJournalVentes(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current']);
        $response = $controller->exportPdf('journal-ventes', $request);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('application/pdf', $response->headers->get('Content-Type'));
    }

    public function testExportPdfCommissionsStripe(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current']);
        $response = $controller->exportPdf('commissions-stripe', $request);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('application/pdf', $response->headers->get('Content-Type'));
    }

    public function testExportPdfCoutsServices(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current']);
        $response = $controller->exportPdf('couts-services', $request);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('application/pdf', $response->headers->get('Content-Type'));
    }

    public function testExportPdfBalanceAgee(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current']);
        $response = $controller->exportPdf('balance-agee', $request);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('application/pdf', $response->headers->get('Content-Type'));
    }

    public function testRapportFinancierReturnsPdf(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current']);
        $response = $controller->rapportFinancier($request);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('application/pdf', $response->headers->get('Content-Type'));
    }

    public function testRapportFinancierPreviousPeriod(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'previous']);
        $response = $controller->rapportFinancier($request);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('application/pdf', $response->headers->get('Content-Type'));
    }

    public function testExportGrandLivreCsv(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current', 'format' => 'csv']);
        $response = $controller->exportGrandLivre($request);
        $this->assertSame(200, $response->getStatusCode());
        $contentType = $response->headers->get('Content-Type') ?? '';
        $this->assertStringContainsString('text/csv', $contentType);
    }

    public function testExportFecCsv(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current', 'format' => 'csv']);
        $response = $controller->exportFec($request);
        $this->assertSame(200, $response->getStatusCode());
        $contentType = $response->headers->get('Content-Type') ?? '';
        $this->assertStringContainsString('text/csv', $contentType);
    }

    public function testExportBalanceAgeeCsv(): void
    {
        $controller = $this->buildController();
        $request = new Request(['format' => 'csv']);
        $response = $controller->exportBalanceAgee($request);
        $this->assertSame(200, $response->getStatusCode());
        $contentType = $response->headers->get('Content-Type') ?? '';
        $this->assertStringContainsString('text/csv', $contentType);
    }

    public function testExportReglementsCsv(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current', 'format' => 'csv']);
        $response = $controller->exportReglements($request);
        $this->assertSame(200, $response->getStatusCode());
        $contentType = $response->headers->get('Content-Type') ?? '';
        $this->assertStringContainsString('text/csv', $contentType);
    }

    public function testExportGrandLivreJson(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportGrandLivre($request);
        $this->assertSame(200, $response->getStatusCode());
        $contentType = $response->headers->get('Content-Type') ?? '';
        $this->assertStringContainsString('application/json', $contentType);
    }

    public function testExportFecJson(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportFec($request);
        $this->assertSame(200, $response->getStatusCode());
        $contentType = $response->headers->get('Content-Type') ?? '';
        $this->assertStringContainsString('application/json', $contentType);
    }

    public function testExportBalanceAgeeJson(): void
    {
        $controller = $this->buildController();
        $request = new Request(['format' => 'json']);
        $response = $controller->exportBalanceAgee($request);
        $this->assertSame(200, $response->getStatusCode());
        $contentType = $response->headers->get('Content-Type') ?? '';
        $this->assertStringContainsString('application/json', $contentType);
    }

    public function testExportReglementsJson(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportReglements($request);
        $this->assertSame(200, $response->getStatusCode());
        $contentType = $response->headers->get('Content-Type') ?? '';
        $this->assertStringContainsString('application/json', $contentType);
    }

    public function testExportPdfFec(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current']);
        $response = $controller->exportPdf('fec', $request);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('application/pdf', $response->headers->get('Content-Type'));
    }

    public function testExportPdfGrandLivre(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current']);
        $response = $controller->exportPdf('grand-livre', $request);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('application/pdf', $response->headers->get('Content-Type'));
    }

    public function testExportPdfReglements(): void
    {
        $controller = $this->buildController();
        $request = new Request(['period' => 'current']);
        $response = $controller->exportPdf('reglements', $request);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('application/pdf', $response->headers->get('Content-Type'));
    }

    public function testExportPdfSignRedirectsToDocuSeal(): void
    {
        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $docuSeal->method('sendComptaForSignature')->willReturn(42);
        $docuSeal->method('getSubmitterSlug')->willReturn('abc123');

        $controller = $this->buildSignController();

        $request = new Request(['period' => 'current']);
        $session = new Session(new MockArraySessionStorage());
        $request->setSession($session);

        $response = $controller->exportPdfSign('journal-ventes', $request, $docuSeal);
        $this->assertSame(302, $response->getStatusCode());
        $this->assertStringContainsString('docuseal.example', $response->headers->get('Location') ?? '');
    }

    public function testExportPdfSignDocuSealNoSlug(): void
    {
        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $docuSeal->method('sendComptaForSignature')->willReturn(42);
        $docuSeal->method('getSubmitterSlug')->willReturn(null);

        $controller = $this->buildSignController();

        $request = new Request(['period' => 'current']);
        $response = $controller->exportPdfSign('journal-ventes', $request, $docuSeal);
        // No slug -> redirect to index
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testSignCallbackWithNoSession(): void
    {
        $controller = $this->buildSignController();
        $request = new Request();
        $session = new Session(new MockArraySessionStorage());
        $request->setSession($session);

        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $mailer = $this->createStub(\App\Service\MailerService::class);
        $twig = $this->createStub(\Twig\Environment::class);

        // No submitter_id in session -> "Session de signature expiree"
        $response = $controller->signCallback('journal-ventes', $request, $docuSeal, $mailer, $twig);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testSignCallbackWithSessionNoPdf(): void
    {
        $controller = $this->buildSignController();
        $request = new Request();
        $session = new Session(new MockArraySessionStorage());
        $session->set('compta_submitter_id', 99);
        $request->setSession($session);

        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $docuSeal->method('getSubmitterData')->willReturn([
            'documents' => [],
            'audit_log_url' => null,
            'metadata' => [],
        ]);

        $mailer = $this->createStub(\App\Service\MailerService::class);
        $twig = $this->createStub(\Twig\Environment::class);
        $twig->method('render')->willReturn('<html></html>');

        $response = $controller->signCallback('journal-ventes', $request, $docuSeal, $mailer, $twig);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testRapportFinancierSignRedirects(): void
    {
        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $docuSeal->method('sendComptaForSignature')->willReturn(10);
        $docuSeal->method('getSubmitterSlug')->willReturn('slug-rap');

        $controller = $this->buildSignController();

        $request = new Request(['period' => 'current']);
        $session = new Session(new MockArraySessionStorage());
        $request->setSession($session);

        $response = $controller->rapportFinancierSign($request, $docuSeal);
        $this->assertSame(302, $response->getStatusCode());
    }

    // ---------------------------------------------------------------
    // Tests avec données réelles pour couvrir les corps de boucles
    // ---------------------------------------------------------------

    /**
     * Construit un EM dont le QueryBuilder renvoie des résultats selon l'ordre des appels.
     * Le premier appel à getResult() renvoie $firstResult, les suivants renvoient $otherResult.
     *
     * @param list<object> $firstResult   résultat du 1er getResult()
     * @param list<object> $otherResult   résultat des appels suivants (défaut: [])
     */
    private function buildEmWithData(array $firstResult, array $otherResult = []): \Doctrine\ORM\EntityManagerInterface
    {
        $stubEm = $this->createStub(\Doctrine\ORM\EntityManagerInterface::class);

        $callCount = 0;
        $query = $this->getMockBuilder(\Doctrine\ORM\Query::class)
            ->setConstructorArgs([$stubEm])
            ->onlyMethods(['getResult', '_doExecute', 'getSQL'])
            ->getMock();
        $query->method('getResult')->willReturnCallback(function () use ($firstResult, $otherResult, &$callCount) {
            ++$callCount;

            return 1 === $callCount ? $firstResult : $otherResult;
        });

        $qb = $this->createStub(\Doctrine\ORM\QueryBuilder::class);
        $qb->method('select')->willReturnSelf();
        $qb->method('from')->willReturnSelf();
        $qb->method('where')->willReturnSelf();
        $qb->method('andWhere')->willReturnSelf();
        $qb->method('setParameter')->willReturnSelf();
        $qb->method('orderBy')->willReturnSelf();
        $qb->method('getQuery')->willReturn($query);

        $em = $this->createStub(\Doctrine\ORM\EntityManagerInterface::class);
        $em->method('createQueryBuilder')->willReturn($qb);

        return $em;
    }

    private function buildPaidFacture(): \App\Entity\Facture
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00001');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('100.00');
        $facture->setTotalTva('20.00');
        $facture->setTotalTtc('120.00');
        $facture->setIsPaid(true);
        $facture->setPaidAt(new \DateTimeImmutable('2026-04-01'));
        $facture->setPaidMethod('card');

        return $facture;
    }

    private function buildUnpaidFacture(): \App\Entity\Facture
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00002');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('50.00');
        $facture->setTotalTva('10.00');
        $facture->setTotalTtc('60.00');

        return $facture;
    }

    private function buildControllerWithData(array $emData, array $otherData = []): \App\Controller\Admin\ComptabiliteController
    {
        $em = !empty($otherData) ? $this->buildEmWithData($emData, $otherData) : $this->buildEmWithData($emData);
        $kernel = $this->buildKernel();
        $svc = new ComptaExportService($em, false, new \App\Service\ComptaHelperService($em));
        $controller = new \App\Controller\Admin\ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc, $this->buildHelper($em));

        $session = new Session(new MockArraySessionStorage());
        $stack = $this->createStub(\Symfony\Component\HttpFoundation\RequestStack::class);
        $stack->method('getSession')->willReturn($session);

        $twig = $this->createStub(\Twig\Environment::class);
        $twig->method('render')->willReturn('<html></html>');

        $router = $this->createStub(\Symfony\Component\Routing\RouterInterface::class);
        $router->method('generate')->willReturn('/admin/comptabilite');

        $container = $this->createStub(\Psr\Container\ContainerInterface::class);
        $container->method('has')->willReturnMap([
            ['twig', true],
            ['router', true],
            ['security.authorization_checker', true],
            ['security.token_storage', true],
            ['request_stack', true],
            ['parameter_bag', true],
            ['serializer', false],
        ]);
        $container->method('get')->willReturnMap([
            ['twig', $twig],
            ['router', $router],
            ['security.authorization_checker', $this->createStub(\Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface::class)],
            ['security.token_storage', $this->createStub(\Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface::class)],
            ['request_stack', $stack],
            ['parameter_bag', $this->createStub(\Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface::class)],
        ]);
        $controller->setContainer($container);

        return $controller;
    }

    private function buildControllerWithTvaAndData(array $emData): \App\Controller\Admin\ComptabiliteController
    {
        $em = $this->buildEmWithData($emData);
        $kernel = $this->buildKernel();
        $svc = new ComptaExportService($em, true, new \App\Service\ComptaHelperService($em));
        $controller = new \App\Controller\Admin\ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc, $this->buildHelper($em));

        $session = new Session(new MockArraySessionStorage());
        $stack = $this->createStub(\Symfony\Component\HttpFoundation\RequestStack::class);
        $stack->method('getSession')->willReturn($session);
        $twig = $this->createStub(\Twig\Environment::class);
        $twig->method('render')->willReturn('<html></html>');
        $container = $this->createStub(\Psr\Container\ContainerInterface::class);
        $container->method('has')->willReturn(false);
        $container->method('get')->willReturnMap([
            ['twig', $twig],
            ['router', $this->createStub(\Symfony\Component\Routing\RouterInterface::class)],
            ['security.authorization_checker', $this->createStub(\Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface::class)],
            ['security.token_storage', $this->createStub(\Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface::class)],
            ['request_stack', $stack],
            ['parameter_bag', $this->createStub(\Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface::class)],
        ]);
        $controller->setContainer($container);

        return $controller;
    }

    public function testExportJournalVentesWithPaidFacture(): void
    {
        $facture = $this->buildPaidFacture();
        $controller = $this->buildControllerWithData([$facture]);

        $request = new Request(['period' => 'current', 'format' => 'csv']);
        $response = $controller->exportJournalVentes($request);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertStringContainsString('text/csv', $response->headers->get('Content-Type') ?? '');
    }

    public function testExportJournalVentesWithTvaEnabled(): void
    {
        $facture = $this->buildPaidFacture();
        $controller = $this->buildControllerWithTvaAndData([$facture]);

        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportJournalVentes($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportGrandLivreWithFacture(): void
    {
        $facture = $this->buildPaidFacture();
        $controller = $this->buildControllerWithData([$facture]);

        $request = new Request(['period' => 'current', 'format' => 'csv']);
        $response = $controller->exportGrandLivre($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportFecWithFacture(): void
    {
        $facture = $this->buildPaidFacture();
        $controller = $this->buildControllerWithData([$facture]);

        $request = new Request(['period' => 'current', 'format' => 'csv']);
        $response = $controller->exportFec($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportBalanceAgeeWithUnpaidFacture(): void
    {
        $facture = $this->buildUnpaidFacture();
        $controller = $this->buildControllerWithData([$facture]);

        $request = new Request(['format' => 'csv']);
        $response = $controller->exportBalanceAgee($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportReglementsWithPaidFacture(): void
    {
        $facture = $this->buildPaidFacture();
        $controller = $this->buildControllerWithData([$facture]);

        $request = new Request(['period' => 'current', 'format' => 'csv']);
        $response = $controller->exportReglements($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportCommissionsStripeWithPayment(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00001');
        $advert = new \App\Entity\Advert($orderNumber, 'secret');

        $user = new \App\Entity\User();
        $user->setEmail('c@t.com');
        $user->setFirstName('A');
        $user->setLastName('B');
        $user->setPassword('h');
        $customer = new \App\Entity\Customer($user);
        $customer->setRaisonSociale('SARL Test');
        $advert->setCustomer($customer);

        $payment = new \App\Entity\AdvertPayment($advert, \App\Entity\AdvertPayment::TYPE_SUCCESS, '120.00');
        $payment->setMethod('card');

        $controller = $this->buildControllerWithData([$payment]);

        $request = new Request(['period' => 'current', 'format' => 'csv']);
        $response = $controller->exportCommissionsStripe($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportCoutsServicesWithFactureAndLine(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00003');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('100.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('100.00');
        $facture->setIsPaid(true);

        $line = new \App\Entity\FactureLine($facture, 'Renouvellement test.fr', '15.00');
        $line->setType('ndd');
        $facture->addLine($line);

        $controller = $this->buildControllerWithData([$facture]);

        $request = new Request(['period' => 'current', 'format' => 'csv']);
        $response = $controller->exportCoutsServices($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportPdfJournalVentesWithFacture(): void
    {
        $facture = $this->buildPaidFacture();
        $controller = $this->buildControllerWithData([$facture]);

        $request = new Request(['period' => 'current']);
        $response = $controller->exportPdf('journal-ventes', $request);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('application/pdf', $response->headers->get('Content-Type'));
    }

    public function testRapportFinancierWithFactureAndPayment(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00004');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('100.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('100.00');
        $facture->setIsPaid(true);

        $line = new \App\Entity\FactureLine($facture, 'Hébergement', '100.00');
        $line->setType('website');
        $facture->addLine($line);

        $advert = new \App\Entity\Advert(new \App\Entity\OrderNumber('04/2026-00004'), 'secret');
        $payment = new \App\Entity\AdvertPayment($advert, \App\Entity\AdvertPayment::TYPE_SUCCESS, '100.00');

        // EM qui renvoie facture pour les factures, et payment pour les AdvertPayment
        $stubEm = $this->createStub(\Doctrine\ORM\EntityManagerInterface::class);

        $queryFacture = $this->getMockBuilder(\Doctrine\ORM\Query::class)
            ->setConstructorArgs([$stubEm])
            ->onlyMethods(['getResult', '_doExecute', 'getSQL'])
            ->getMock();
        $queryFacture->method('getResult')->willReturn([$facture]);

        $queryPayment = $this->getMockBuilder(\Doctrine\ORM\Query::class)
            ->setConstructorArgs([$stubEm])
            ->onlyMethods(['getResult', '_doExecute', 'getSQL'])
            ->getMock();
        $queryPayment->method('getResult')->willReturn([$payment]);

        $callCount = 0;
        $qb = $this->createStub(\Doctrine\ORM\QueryBuilder::class);
        $qb->method('select')->willReturnSelf();
        $qb->method('from')->willReturnSelf();
        $qb->method('where')->willReturnSelf();
        $qb->method('andWhere')->willReturnSelf();
        $qb->method('setParameter')->willReturnSelf();
        $qb->method('orderBy')->willReturnSelf();
        // Alternate: first call returns facture query, second returns payment query
        $qb->method('getQuery')->willReturnCallback(function () use ($queryFacture, $queryPayment, &$callCount) {
            ++$callCount;

            return 1 === $callCount ? $queryFacture : $queryPayment;
        });

        $em = $this->createStub(\Doctrine\ORM\EntityManagerInterface::class);
        $em->method('createQueryBuilder')->willReturn($qb);

        $kernel = $this->buildKernel();
        $svc = new ComptaExportService($em, false, new \App\Service\ComptaHelperService($em));
        $controller = new \App\Controller\Admin\ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc, $this->buildHelper($em));

        $session = new Session(new MockArraySessionStorage());
        $stack = $this->createStub(\Symfony\Component\HttpFoundation\RequestStack::class);
        $stack->method('getSession')->willReturn($session);
        $twig = $this->createStub(\Twig\Environment::class);
        $twig->method('render')->willReturn('<html></html>');
        $container = $this->createStub(\Psr\Container\ContainerInterface::class);
        $container->method('has')->willReturn(false);
        $container->method('get')->willReturnMap([
            ['twig', $twig],
            ['router', $this->createStub(\Symfony\Component\Routing\RouterInterface::class)],
            ['security.authorization_checker', $this->createStub(\Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface::class)],
            ['security.token_storage', $this->createStub(\Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface::class)],
            ['request_stack', $stack],
            ['parameter_bag', $this->createStub(\Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface::class)],
        ]);
        $controller->setContainer($container);

        $request = new Request(['period' => 'current']);
        $response = $controller->rapportFinancier($request);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('application/pdf', $response->headers->get('Content-Type'));
    }

    public function testExportPdfSignNoSlugRedirectsToIndex(): void
    {
        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $docuSeal->method('sendComptaForSignature')->willReturn(42);
        $docuSeal->method('getSubmitterSlug')->willReturn(null);

        $controller = $this->buildSignController();

        $request = new Request(['period' => 'current']);
        $session = new Session(new MockArraySessionStorage());
        $request->setSession($session);

        $response = $controller->exportPdfSign('grand-livre', $request, $docuSeal);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testExportPdfSignNullSubmitterIdRedirectsToIndex(): void
    {
        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $docuSeal->method('sendComptaForSignature')->willReturn(null);

        $controller = $this->buildSignController();

        $request = new Request(['period' => 'current']);
        $session = new Session(new MockArraySessionStorage());
        $request->setSession($session);

        $response = $controller->exportPdfSign('fec', $request, $docuSeal);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testSignCallbackWithSessionAndPdfDocument(): void
    {
        $controller = $this->buildSignController();

        $request = new Request();
        $session = new Session(new MockArraySessionStorage());
        $session->set('compta_submitter_id', 77);
        $request->setSession($session);

        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $docuSeal->method('getSubmitterData')->willReturn([
            'documents' => [['url' => 'http://example.com/fake.pdf']],
            'audit_log_url' => null,
            'metadata' => ['period_from' => '2026-01-01', 'period_to' => '2026-03-31'],
        ]);

        $mailer = $this->createStub(\App\Service\MailerService::class);
        $twig = $this->createStub(\Twig\Environment::class);
        $twig->method('render')->willReturn('<html></html>');

        $response = $controller->signCallback('journal-ventes', $request, $docuSeal, $mailer, $twig);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testRapportFinancierSignNoSlug(): void
    {
        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $docuSeal->method('sendComptaForSignature')->willReturn(null);

        $controller = $this->buildSignController();

        $request = new Request(['period' => 'current']);
        $session = new Session(new MockArraySessionStorage());
        $request->setSession($session);

        $response = $controller->rapportFinancierSign($request, $docuSeal);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testExportCoutsServicesWithNddGestionLine(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00005');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('10.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('10.00');
        $facture->setIsPaid(true);

        // NDD gestion line (not Renouvellement or Depot) - should NOT increment lines counter
        $line = new \App\Entity\FactureLine($facture, 'Gestion DNS', '10.00');
        $line->setType('ndd');
        $facture->addLine($line);

        $controller = $this->buildControllerWithData([$facture]);

        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportCoutsServices($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportCoutsServicesWithUnknownLineType(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00006');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('10.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('10.00');
        $facture->setIsPaid(true);

        $line = new \App\Entity\FactureLine($facture, 'Service inconnu', '10.00');
        $line->setType('unknown_type');
        $facture->addLine($line);

        $controller = $this->buildControllerWithData([$facture]);

        $request = new Request(['period' => 'current', 'format' => 'csv']);
        $response = $controller->exportCoutsServices($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    // ---------------------------------------------------------------
    // signCallback — session present, PDF URL returns valid PDF bytes
    // ---------------------------------------------------------------

    /**
     * Test the signCallback path where file_get_contents returns a valid PDF,
     * so $attachments is non-empty and the mailer sendEmail branch is executed.
     */
    public function testSignCallbackWithSessionAndRealPdfContent(): void
    {
        // Write a minimal fake PDF file to a temp URL-accessible path
        $tmpPdf = tempnam(sys_get_temp_dir(), 'compta_test_').'.pdf';
        file_put_contents($tmpPdf, '%PDF-1.4 fake pdf content');

        $controller = $this->buildSignController();

        $request = new Request();
        $session = new Session(new MockArraySessionStorage());
        $session->set('compta_submitter_id', 88);
        $request->setSession($session);

        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $docuSeal->method('getSubmitterData')->willReturn([
            'documents' => [['url' => 'file://'.$tmpPdf]],
            'audit_log_url' => null,
            'metadata' => ['period_from' => '2026-01-01', 'period_to' => '2026-03-31'],
        ]);

        $mailer = $this->createStub(\App\Service\MailerService::class);
        $twig = $this->createStub(\Twig\Environment::class);
        $twig->method('render')->willReturn('<html></html>');

        $response = $controller->signCallback('journal-ventes', $request, $docuSeal, $mailer, $twig);

        @unlink($tmpPdf);

        $this->assertSame(302, $response->getStatusCode());
    }

    /**
     * Test signCallback where both PDF and audit log are provided as valid files.
     */
    public function testSignCallbackWithSessionAndAuditLog(): void
    {
        $tmpPdf = tempnam(sys_get_temp_dir(), 'compta_pdf_').'.pdf';
        file_put_contents($tmpPdf, '%PDF-1.4 pdf');

        $tmpAudit = tempnam(sys_get_temp_dir(), 'compta_audit_').'.pdf';
        file_put_contents($tmpAudit, 'audit log content');

        $controller = $this->buildSignController();

        $request = new Request();
        $session = new Session(new MockArraySessionStorage());
        $session->set('compta_submitter_id', 55);
        $request->setSession($session);

        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $docuSeal->method('getSubmitterData')->willReturn([
            'documents' => [['url' => 'file://'.$tmpPdf]],
            'audit_log_url' => 'file://'.$tmpAudit,
            'metadata' => ['period_from' => '2026-01-01', 'period_to' => '2026-03-31'],
        ]);

        $mailer = $this->createStub(\App\Service\MailerService::class);
        $twig = $this->createStub(\Twig\Environment::class);
        $twig->method('render')->willReturn('<html></html>');

        $response = $controller->signCallback('rapport-financier', $request, $docuSeal, $mailer, $twig);

        @unlink($tmpPdf);
        @unlink($tmpAudit);

        $this->assertSame(302, $response->getStatusCode());
    }

    // ---------------------------------------------------------------
    // rapportFinancierSign — various extra paths
    // ---------------------------------------------------------------

    /**
     * rapportFinancierSign — submitter ID returned but no slug → redirect to index.
     */
    public function testRapportFinancierSignNoSlugReturned(): void
    {
        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $docuSeal->method('sendComptaForSignature')->willReturn(20);
        $docuSeal->method('getSubmitterSlug')->willReturn(null);

        $controller = $this->buildSignController();

        $request = new Request(['period' => 'previous']);
        $session = new Session(new MockArraySessionStorage());
        $request->setSession($session);

        $response = $controller->rapportFinancierSign($request, $docuSeal);
        $this->assertSame(302, $response->getStatusCode());
    }

    /**
     * rapportFinancierSign — submitter ID returned with valid slug → redirect to DocuSeal.
     */
    public function testRapportFinancierSignRedirectsToDocuSeal(): void
    {
        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $docuSeal->method('sendComptaForSignature')->willReturn(30);
        $docuSeal->method('getSubmitterSlug')->willReturn('slug-fin');

        $controller = $this->buildSignController();

        $request = new Request(['period' => 'custom', 'from' => '2026-01-01', 'to' => '2026-03-31']);
        $session = new Session(new MockArraySessionStorage());
        $request->setSession($session);

        $response = $controller->rapportFinancierSign($request, $docuSeal);
        $this->assertSame(302, $response->getStatusCode());
        $this->assertStringContainsString('docuseal.example', $response->headers->get('Location') ?? '');
    }

    // ---------------------------------------------------------------
    // buildFecData — covers paid-branch rows (FEC has debit/credit pair)
    // ---------------------------------------------------------------

    public function testExportFecWithTvaEnabledAndPaidFacture(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00010');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('100.00');
        $facture->setTotalTva('20.00');
        $facture->setTotalTtc('120.00');
        $facture->setIsPaid(true);
        $facture->setPaidAt(new \DateTimeImmutable('2026-04-01'));
        $facture->setPaidMethod('sepa');

        $controller = $this->buildControllerWithTvaAndData([$facture]);

        $request = new Request(['period' => 'current', 'format' => 'csv']);
        $response = $controller->exportFec($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    // ---------------------------------------------------------------
    // exportCommissionsStripe — covers customer_balance and klarna methods
    // ---------------------------------------------------------------

    public function testExportCommissionsStripeWithCustomerBalanceMethod(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00011');
        $advert = new \App\Entity\Advert($orderNumber, 'secret');

        $user = new \App\Entity\User();
        $user->setEmail('cb@t.com');
        $user->setFirstName('C');
        $user->setLastName('B');
        $user->setPassword('h');
        $customer = new \App\Entity\Customer($user);
        $advert->setCustomer($customer);

        $payment = new \App\Entity\AdvertPayment($advert, \App\Entity\AdvertPayment::TYPE_SUCCESS, '250.00');
        $payment->setMethod('customer_balance');

        $controller = $this->buildControllerWithData([$payment]);

        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportCommissionsStripe($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    // ---------------------------------------------------------------
    // exportPdfSign — cover all types including balance-agee and reglements
    // ---------------------------------------------------------------

    public function testExportPdfSignBalanceAgeeRedirects(): void
    {
        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $docuSeal->method('sendComptaForSignature')->willReturn(50);
        $docuSeal->method('getSubmitterSlug')->willReturn('slug-bal');

        $controller = $this->buildSignController();
        $request = new Request(['period' => 'current']);
        $session = new Session(new MockArraySessionStorage());
        $request->setSession($session);

        $response = $controller->exportPdfSign('balance-agee', $request, $docuSeal);
        $this->assertSame(302, $response->getStatusCode());
    }

    public function testExportPdfSignReglementsRedirects(): void
    {
        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $docuSeal->method('sendComptaForSignature')->willReturn(60);
        $docuSeal->method('getSubmitterSlug')->willReturn('slug-reg');

        $controller = $this->buildSignController();
        $request = new Request(['period' => 'current']);
        $session = new Session(new MockArraySessionStorage());
        $request->setSession($session);

        $response = $controller->exportPdfSign('reglements', $request, $docuSeal);
        $this->assertSame(302, $response->getStatusCode());
    }

    // ---------------------------------------------------------------
    // resolveCompteBanque / resolveLibelleBanque — non-card payment methods
    // ---------------------------------------------------------------

    public function testExportReglementsWithSepaDebitMethod(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00020');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('80.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('80.00');
        $facture->setIsPaid(true);
        $facture->setPaidAt(new \DateTimeImmutable('2026-04-01'));
        $facture->setPaidMethod('sepa_debit');

        $controller = $this->buildControllerWithData([$facture]);
        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportReglements($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportReglementsWithPaypalMethod(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00021');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('90.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('90.00');
        $facture->setIsPaid(true);
        $facture->setPaidAt(new \DateTimeImmutable('2026-04-01'));
        $facture->setPaidMethod('paypal');

        $controller = $this->buildControllerWithData([$facture]);
        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportReglements($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportReglementsWithKlarnaMethod(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00022');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('95.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('95.00');
        $facture->setIsPaid(true);
        $facture->setPaidAt(new \DateTimeImmutable('2026-04-01'));
        $facture->setPaidMethod('klarna');

        $controller = $this->buildControllerWithData([$facture]);
        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportReglements($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportReglementsWithVirementMethod(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00023');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('200.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('200.00');
        $facture->setIsPaid(true);
        $facture->setPaidAt(new \DateTimeImmutable('2026-04-01'));
        $facture->setPaidMethod('virement');

        $controller = $this->buildControllerWithData([$facture]);
        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportReglements($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportJournalVentesWithSepaDebitPaidFacture(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00024');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('150.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('150.00');
        $facture->setIsPaid(true);
        $facture->setPaidAt(new \DateTimeImmutable('2026-04-01'));
        $facture->setPaidMethod('sepa_debit');

        $controller = $this->buildControllerWithData([$facture]);
        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportJournalVentes($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportJournalVentesWithPaypalPaidFacture(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00025');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('75.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('75.00');
        $facture->setIsPaid(true);
        $facture->setPaidAt(new \DateTimeImmutable('2026-04-01'));
        $facture->setPaidMethod('paypal');

        $controller = $this->buildControllerWithData([$facture]);
        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportJournalVentes($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportJournalVentesWithKlarnaPaidFacture(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00026');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('85.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('85.00');
        $facture->setIsPaid(true);
        $facture->setPaidAt(new \DateTimeImmutable('2026-04-01'));
        $facture->setPaidMethod('klarna');

        $controller = $this->buildControllerWithData([$facture]);
        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportJournalVentes($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testExportJournalVentesWithVirementPaidFacture(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00027');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('500.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('500.00');
        $facture->setIsPaid(true);
        $facture->setPaidAt(new \DateTimeImmutable('2026-04-01'));
        $facture->setPaidMethod('transfer');

        $controller = $this->buildControllerWithData([$facture]);
        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportJournalVentes($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    // ---------------------------------------------------------------
    // resolveTrancheAge — > 30, > 60, > 90 day brackets
    // ---------------------------------------------------------------

    public function testBalanceAgeeWith45DayOldFacture(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00030');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('60.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('60.00');
        // Create a facture 45 days ago to hit the "31-60 jours" tranche
        $ref = new \ReflectionProperty(\App\Entity\Facture::class, 'createdAt');
        $ref->setAccessible(true);
        $ref->setValue($facture, new \DateTimeImmutable('-45 days'));

        $controller = $this->buildControllerWithData([$facture]);
        $request = new Request(['format' => 'json']);
        $response = $controller->exportBalanceAgee($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testBalanceAgeeWith75DayOldFacture(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00031');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('70.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('70.00');
        // Create a facture 75 days ago to hit the "61-90 jours" tranche
        $ref = new \ReflectionProperty(\App\Entity\Facture::class, 'createdAt');
        $ref->setAccessible(true);
        $ref->setValue($facture, new \DateTimeImmutable('-75 days'));

        $controller = $this->buildControllerWithData([$facture]);
        $request = new Request(['format' => 'json']);
        $response = $controller->exportBalanceAgee($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    public function testBalanceAgeeWith100DayOldFacture(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00032');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('80.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('80.00');
        // Create a facture 100 days ago to hit the "+90 jours" tranche
        $ref = new \ReflectionProperty(\App\Entity\Facture::class, 'createdAt');
        $ref->setAccessible(true);
        $ref->setValue($facture, new \DateTimeImmutable('-100 days'));

        $controller = $this->buildControllerWithData([$facture]);
        $request = new Request(['format' => 'json']);
        $response = $controller->exportBalanceAgee($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    // ---------------------------------------------------------------
    // buildCoutsServicesData — covers prestataire grouping loop
    // ---------------------------------------------------------------

    public function testExportCoutsServicesWithPrestataire(): void
    {
        $prestataire = new \App\Entity\Prestataire('ACME Hosting');
        $ref = new \ReflectionProperty(\App\Entity\Prestataire::class, 'id');
        $ref->setAccessible(true);
        $ref->setValue($prestataire, 10);

        $facture = new \App\Entity\FacturePrestataire($prestataire, 'PRESTA-001', 2026, 4);
        $facture->setMontantHt('500.00');
        $facture->setMontantTtc('600.00');

        // EM returns [] for factures query (1st), then [$facture] for facturesPresta (2nd)
        $controller = $this->buildControllerWithData([], [$facture]);

        $request = new Request(['period' => 'current', 'format' => 'json']);
        $response = $controller->exportCoutsServices($request);
        $this->assertSame(200, $response->getStatusCode());
        $data = json_decode($response->getContent(), true);
        // Should contain a row for the prestataire
        $names = array_column($data, 'Service');
        $this->assertContains('Prestataire : ACME Hosting', $names);
    }

    // ---------------------------------------------------------------
    // rapportFinancier — unknown line type falls back to "other"
    // ---------------------------------------------------------------

    public function testRapportFinancierWithUnknownLineType(): void
    {
        $orderNumber = new \App\Entity\OrderNumber('04/2026-00040');
        $facture = new \App\Entity\Facture($orderNumber, 'secret');
        $facture->setTotalHt('50.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('50.00');
        $facture->setIsPaid(true);

        // Line with a type NOT in SERVICE_COSTS → fallback to 'other' (line 400)
        $line = new \App\Entity\FactureLine($facture, 'Prestation speciale', '50.00');
        $line->setType('completely_unknown_type');
        $facture->addLine($line);

        // Two-query EM: first for factures in rapportFinancier, second for payments
        $stubEm = $this->createStub(\Doctrine\ORM\EntityManagerInterface::class);

        $queryFacture = $this->getMockBuilder(\Doctrine\ORM\Query::class)
            ->setConstructorArgs([$stubEm])
            ->onlyMethods(['getResult', '_doExecute', 'getSQL'])
            ->getMock();
        $queryFacture->method('getResult')->willReturn([$facture]);

        $queryEmpty = $this->getMockBuilder(\Doctrine\ORM\Query::class)
            ->setConstructorArgs([$stubEm])
            ->onlyMethods(['getResult', '_doExecute', 'getSQL'])
            ->getMock();
        $queryEmpty->method('getResult')->willReturn([]);

        $callCount = 0;
        $qb = $this->createStub(\Doctrine\ORM\QueryBuilder::class);
        $qb->method('select')->willReturnSelf();
        $qb->method('from')->willReturnSelf();
        $qb->method('where')->willReturnSelf();
        $qb->method('andWhere')->willReturnSelf();
        $qb->method('setParameter')->willReturnSelf();
        $qb->method('orderBy')->willReturnSelf();
        $qb->method('getQuery')->willReturnCallback(function () use ($queryFacture, $queryEmpty, &$callCount) {
            ++$callCount;

            return 1 === $callCount ? $queryFacture : $queryEmpty;
        });

        $em = $this->createStub(\Doctrine\ORM\EntityManagerInterface::class);
        $em->method('createQueryBuilder')->willReturn($qb);

        $kernel = $this->buildKernel();
        $svc = new ComptaExportService($em, false, new \App\Service\ComptaHelperService($em));
        $controller = new \App\Controller\Admin\ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc, $this->buildHelper($em));

        $session = new Session(new MockArraySessionStorage());
        $stack = $this->createStub(\Symfony\Component\HttpFoundation\RequestStack::class);
        $stack->method('getSession')->willReturn($session);
        $twig = $this->createStub(\Twig\Environment::class);
        $twig->method('render')->willReturn('<html></html>');
        $container = $this->createStub(\Psr\Container\ContainerInterface::class);
        $container->method('has')->willReturn(false);
        $container->method('get')->willReturnMap([
            ['twig', $twig],
            ['router', $this->createStub(\Symfony\Component\Routing\RouterInterface::class)],
            ['security.authorization_checker', $this->createStub(\Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface::class)],
            ['security.token_storage', $this->createStub(\Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface::class)],
            ['request_stack', $stack],
            ['parameter_bag', $this->createStub(\Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface::class)],
        ]);
        $controller->setContainer($container);

        $request = new Request(['period' => 'current']);
        $response = $controller->rapportFinancier($request);
        $this->assertSame(200, $response->getStatusCode());
    }

    /**
     * signCallback — covers the sendSignedDocumentEmail branches where metadata keys
     * 'period_from' and 'period_to' are absent, so $periodFrom and $periodTo fall back to ''.
     */
    public function testSignCallbackWithSessionAndPdfNoMetadataPeriods(): void
    {
        $tmpPdf = tempnam(sys_get_temp_dir(), 'compta_nometa_').'.pdf';
        file_put_contents($tmpPdf, '%PDF-1.4 pdf');

        $controller = $this->buildSignController();

        $request = new Request();
        $session = new Session(new MockArraySessionStorage());
        $session->set('compta_submitter_id', 111);
        $request->setSession($session);

        $docuSeal = $this->createStub(\App\Service\DocuSealService::class);
        $docuSeal->method('getSubmitterData')->willReturn([
            'documents' => [['url' => 'file://'.$tmpPdf]],
            'audit_log_url' => null,
            // metadata has NO period_from / period_to keys → covers the '' fallback branches
            'metadata' => [],
        ]);

        $mailer = $this->createStub(\App\Service\MailerService::class);
        $twig = $this->createStub(\Twig\Environment::class);
        $twig->method('render')->willReturn('<html></html>');

        $response = $controller->signCallback('journal-ventes', $request, $docuSeal, $mailer, $twig);

        @unlink($tmpPdf);

        $this->assertSame(302, $response->getStatusCode());
    }
}
