<?php

namespace App\Tests\Controller\Admin;

use App\Controller\Admin\FactureController;
use App\Entity\Customer;
use App\Entity\Facture;
use App\Service\MeilisearchService;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
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\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Twig\Environment;

class FactureControllerTest extends TestCase
{
    private function buildController(?EntityManagerInterface $em = null, ?MeilisearchService $meilisearch = null): FactureController
    {
        $em ??= $this->createStub(EntityManagerInterface::class);
        $meilisearch ??= $this->createStub(MeilisearchService::class);

        $controller = new FactureController($em, $meilisearch);

        $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>');

        $router = $this->createStub(RouterInterface::class);
        $router->method('generate')->willReturn('/redirect');

        $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', $this->createStub(TokenStorageInterface::class)],
            ['request_stack', $stack],
            ['parameter_bag', $this->createStub(ParameterBagInterface::class)],
        ]);
        $controller->setContainer($container);

        return $controller;
    }

    // ---------------------------------------------------------------
    // search
    // ---------------------------------------------------------------

    public function testSearchReturnsEmptyWhenQueryBlank(): void
    {
        $controller = $this->buildController();
        $request = new Request(['q' => '']);
        $response = $controller->search(1, $request);

        $this->assertInstanceOf(JsonResponse::class, $response);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertSame('[]', $response->getContent());
    }

    public function testSearchReturnsMeilisearchResults(): void
    {
        $hits = [['id' => 1, 'invoiceNumber' => 'F-2026-001']];

        $meilisearch = $this->createStub(MeilisearchService::class);
        $meilisearch->method('searchFactures')->willReturn($hits);

        $controller = $this->buildController(null, $meilisearch);
        $request = new Request(['q' => 'F-2026']);
        $response = $controller->search(1, $request);

        $this->assertInstanceOf(JsonResponse::class, $response);
        $this->assertSame(200, $response->getStatusCode());

        $data = json_decode($response->getContent(), true);
        $this->assertCount(1, $data);
        $this->assertSame('F-2026-001', $data[0]['invoiceNumber']);
    }

    public function testSearchPassesCustomerIdFilter(): void
    {
        $meilisearch = $this->createMock(MeilisearchService::class);
        $meilisearch->expects($this->once())
            ->method('searchFactures')
            ->with('test', 20, 42)
            ->willReturn([]);

        $controller = $this->buildController(null, $meilisearch);
        $request = new Request(['q' => 'test']);
        $response = $controller->search(42, $request);

        $this->assertInstanceOf(JsonResponse::class, $response);
    }

    // ---------------------------------------------------------------
    // send — facture with no PDF
    // ---------------------------------------------------------------

    public function testSendRedirectsWhenNoPdfGenerated(): void
    {
        $customer = $this->createStub(Customer::class);
        $customer->method('getId')->willReturn(5);

        $facture = $this->createStub(Facture::class);
        $facture->method('getFacturePdf')->willReturn(null);
        $facture->method('getCustomer')->willReturn($customer);

        $factureRepo = $this->createStub(EntityRepository::class);
        $factureRepo->method('find')->willReturn($facture);

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

        $controller = $this->buildController($em);

        $mailer = $this->createStub(\App\Service\MailerService::class);
        $twig = $this->createStub(Environment::class);
        $urlGenerator = $this->createStub(\Symfony\Component\Routing\Generator\UrlGeneratorInterface::class);
        $urlGenerator->method('generate')->willReturn('http://localhost/facture/verify/1/abc');

        $response = $controller->send(1, $mailer, $twig, $urlGenerator, '/tmp');

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

    public function testSendThrows404WhenFactureNotFound(): void
    {
        $factureRepo = $this->createStub(EntityRepository::class);
        $factureRepo->method('find')->willReturn(null);

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

        $controller = $this->buildController($em);

        $mailer = $this->createStub(\App\Service\MailerService::class);
        $twig = $this->createStub(Environment::class);
        $urlGenerator = $this->createStub(\Symfony\Component\Routing\Generator\UrlGeneratorInterface::class);

        $this->expectException(\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class);
        $controller->send(999, $mailer, $twig, $urlGenerator, '/tmp');
    }

    // ---------------------------------------------------------------
    // send — successful path with PDF file actually on disk
    // ---------------------------------------------------------------

    public function testSendSuccessfullyWithPdfFileOnDisk(): void
    {
        // Create a real directory and PDF file so file_exists($pdfPath) returns true
        $tmpDir = sys_get_temp_dir().'/facture_send_test_'.uniqid();
        mkdir($tmpDir.'/public/uploads/factures', 0777, true);
        $pdfFileName = 'test-facture.pdf';
        file_put_contents($tmpDir.'/public/uploads/factures/'.$pdfFileName, '%PDF-1.4 test');

        $customer = $this->createStub(\App\Entity\Customer::class);
        $customer->method('getId')->willReturn(7);
        $customer->method('getEmail')->willReturn('pdf@test.com');

        $facture = $this->createStub(\App\Entity\Facture::class);
        $facture->method('getFacturePdf')->willReturn($pdfFileName);
        $facture->method('getCustomer')->willReturn($customer);
        $facture->method('getInvoiceNumber')->willReturn('F-2026-099');
        $facture->method('getId')->willReturn(99);
        $facture->method('getHmac')->willReturn('hmac99');

        $factureRepo = $this->createStub(EntityRepository::class);
        $factureRepo->method('find')->willReturn($facture);

        $em = $this->createMock(EntityManagerInterface::class);
        $em->method('getRepository')->willReturn($factureRepo);
        $em->expects($this->once())->method('flush');

        $controller = $this->buildController($em);

        $mailer = $this->createStub(\App\Service\MailerService::class);
        $twig = $this->createStub(\Twig\Environment::class);
        $twig->method('render')->willReturn('<html></html>');
        $urlGenerator = $this->createStub(\Symfony\Component\Routing\Generator\UrlGeneratorInterface::class);
        $urlGenerator->method('generate')->willReturn('http://localhost/facture/verify/99/hmac99');

        $response = $controller->send(99, $mailer, $twig, $urlGenerator, $tmpDir);

        // Cleanup
        @unlink($tmpDir.'/public/uploads/factures/'.$pdfFileName);
        @rmdir($tmpDir.'/public/uploads/factures');
        @rmdir($tmpDir.'/public/uploads');
        @rmdir($tmpDir.'/public');
        @rmdir($tmpDir);

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

    // ---------------------------------------------------------------
    // generatePdf — 404 when facture not found
    // ---------------------------------------------------------------

    public function testGeneratePdfThrows404WhenFactureNotFound(): void
    {
        $factureRepo = $this->createStub(EntityRepository::class);
        $factureRepo->method('find')->willReturn(null);

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

        $controller = $this->buildController($em);

        $this->expectException(\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class);
        $controller->generatePdf(
            999,
            $this->createStub(\Symfony\Component\HttpKernel\KernelInterface::class),
            $this->createStub(\Symfony\Component\Routing\Generator\UrlGeneratorInterface::class),
            $this->createStub(\Twig\Environment::class),
        );
    }

    // ---------------------------------------------------------------
    // send — successful full path (pdf exists, customer has email)
    // ---------------------------------------------------------------

    public function testSendSuccessfully(): void
    {
        $customer = $this->createStub(\App\Entity\Customer::class);
        $customer->method('getId')->willReturn(5);
        $customer->method('getEmail')->willReturn('client@test.com');

        $facture = $this->createStub(\App\Entity\Facture::class);
        $facture->method('getFacturePdf')->willReturn('facture-test.pdf');
        $facture->method('getCustomer')->willReturn($customer);
        $facture->method('getInvoiceNumber')->willReturn('F-2026-001');
        $facture->method('getId')->willReturn(1);
        $facture->method('getHmac')->willReturn('abc123');

        $factureRepo = $this->createStub(EntityRepository::class);
        $factureRepo->method('find')->willReturn($facture);

        $em = $this->createMock(EntityManagerInterface::class);
        $em->method('getRepository')->willReturn($factureRepo);
        $em->expects($this->once())->method('flush');

        $controller = $this->buildController($em);

        $mailer = $this->createStub(\App\Service\MailerService::class);
        $twig = $this->createStub(\Twig\Environment::class);
        $twig->method('render')->willReturn('<html></html>');
        $urlGenerator = $this->createStub(\Symfony\Component\Routing\Generator\UrlGeneratorInterface::class);
        $urlGenerator->method('generate')->willReturn('http://localhost/facture/verify/1/abc');

        // projectDir points to a tmp dir where factures/ path won't exist (no attachment)
        $response = $controller->send(1, $mailer, $twig, $urlGenerator, sys_get_temp_dir());

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

    public function testSendRedirectsWhenCustomerHasNoEmail(): void
    {
        $customer = $this->createStub(Customer::class);
        $customer->method('getId')->willReturn(5);
        $customer->method('getEmail')->willReturn(null);

        $facture = $this->createStub(Facture::class);
        $facture->method('getFacturePdf')->willReturn('facture.pdf');
        $facture->method('getCustomer')->willReturn($customer);

        $factureRepo = $this->createStub(EntityRepository::class);
        $factureRepo->method('find')->willReturn($facture);

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

        $controller = $this->buildController($em);

        $mailer = $this->createStub(\App\Service\MailerService::class);
        $twig = $this->createStub(Environment::class);
        $urlGenerator = $this->createStub(\Symfony\Component\Routing\Generator\UrlGeneratorInterface::class);

        $response = $controller->send(1, $mailer, $twig, $urlGenerator, '/tmp');

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

    // ---------------------------------------------------------------
    // send — customer is null
    // ---------------------------------------------------------------

    public function testSendRedirectsWhenCustomerIsNull(): void
    {
        $facture = $this->createStub(Facture::class);
        $facture->method('getFacturePdf')->willReturn('facture.pdf');
        $facture->method('getCustomer')->willReturn(null);

        $factureRepo = $this->createStub(EntityRepository::class);
        $factureRepo->method('find')->willReturn($facture);

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

        $controller = $this->buildController($em);

        $mailer = $this->createStub(\App\Service\MailerService::class);
        $twig = $this->createStub(Environment::class);
        $urlGenerator = $this->createStub(\Symfony\Component\Routing\Generator\UrlGeneratorInterface::class);

        $response = $controller->send(1, $mailer, $twig, $urlGenerator, '/tmp');

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

    // ---------------------------------------------------------------
    // generatePdf — facture has existing PDF (hadOld = true branch)
    // ---------------------------------------------------------------

    public function testGeneratePdfThrows404WhenFactureHasOldPdfPath(): void
    {
        // When facture is not found regardless of prior PDF state, 404 is thrown.
        // This test validates the 404 path mirrors both old and new PDF state,
        // ensuring the repository lookup is the entry gate.
        $factureRepo = $this->createStub(EntityRepository::class);
        $factureRepo->method('find')->willReturn(null);

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

        $controller = $this->buildController($em);

        $this->expectException(\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class);
        $controller->generatePdf(
            0,
            $this->createStub(\Symfony\Component\HttpKernel\KernelInterface::class),
            $this->createStub(\Symfony\Component\Routing\Generator\UrlGeneratorInterface::class),
            $this->createStub(\Twig\Environment::class),
        );
    }

    // ---------------------------------------------------------------
    // generatePdf — success path (facture found, PDF generated via FPDF)
    // ---------------------------------------------------------------

    public function testGeneratePdfSuccessPath(): void
    {
        $tmpDir = sys_get_temp_dir().'/facture_test_'.uniqid();
        mkdir($tmpDir.'/public/uploads/factures', 0777, true);

        $orderNumber = new \App\Entity\OrderNumber('04/2026-00099');
        $facture = new \App\Entity\Facture($orderNumber, 'test_secret');
        $facture->setTotalHt('100.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('100.00');

        $factureRepo = $this->createStub(EntityRepository::class);
        $factureRepo->method('find')->willReturn($facture);

        $em = $this->createMock(EntityManagerInterface::class);
        $em->method('getRepository')->willReturn($factureRepo);
        $em->expects($this->once())->method('flush');

        $controller = $this->buildController($em);

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

        $urlGenerator = $this->createStub(\Symfony\Component\Routing\Generator\UrlGeneratorInterface::class);
        $urlGenerator->method('generate')->willReturn('http://localhost/facture/verify/1/abc');

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

        $response = $controller->generatePdf(1, $kernel, $urlGenerator, $twig);

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

        // Cleanup
        array_map('unlink', glob($tmpDir.'/public/uploads/factures/*') ?: []);
        @rmdir($tmpDir.'/public/uploads/factures');
        @rmdir($tmpDir.'/public/uploads');
        @rmdir($tmpDir.'/public');
        @rmdir($tmpDir);
    }

    public function testGeneratePdfWithExistingPdf(): void
    {
        $tmpDir = sys_get_temp_dir().'/facture_test_old_'.uniqid();
        mkdir($tmpDir.'/public/uploads/factures', 0777, true);

        // Create an "old" PDF file to test the $hadOld = true branch
        $oldPdfName = 'old-facture.pdf';
        $oldPdfPath = $tmpDir.'/public/uploads/factures/'.$oldPdfName;
        file_put_contents($oldPdfPath, '%PDF-1.4 old content');

        $orderNumber = new \App\Entity\OrderNumber('04/2026-00098');
        $facture = new \App\Entity\Facture($orderNumber, 'secret2');
        $facture->setTotalHt('50.00');
        $facture->setTotalTva('0.00');
        $facture->setTotalTtc('50.00');
        // Simulate facture that already has a PDF filename set
        $facture->setFacturePdf($oldPdfName);

        $factureRepo = $this->createStub(EntityRepository::class);
        $factureRepo->method('find')->willReturn($facture);

        $em = $this->createMock(EntityManagerInterface::class);
        $em->method('getRepository')->willReturn($factureRepo);
        $em->expects($this->once())->method('flush');

        $controller = $this->buildController($em);

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

        $urlGenerator = $this->createStub(\Symfony\Component\Routing\Generator\UrlGeneratorInterface::class);
        $urlGenerator->method('generate')->willReturn('http://localhost/facture/verify/1/abc');

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

        $response = $controller->generatePdf(1, $kernel, $urlGenerator, $twig);

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

        // Cleanup
        array_map('unlink', glob($tmpDir.'/public/uploads/factures/*') ?: []);
        @rmdir($tmpDir.'/public/uploads/factures');
        @rmdir($tmpDir.'/public/uploads');
        @rmdir($tmpDir.'/public');
        @rmdir($tmpDir);
    }
}
