<?php

namespace App\Tests\Service\Pdf;

use App\Service\Pdf\ComptaPdf;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpKernel\KernelInterface;

class ComptaPdfTest extends TestCase
{
    private KernelInterface $kernel;
    private string $projectDir;

    protected function setUp(): void
    {
        $this->projectDir = sys_get_temp_dir().'/compta-pdf-test-'.bin2hex(random_bytes(4));
        mkdir($this->projectDir.'/public', 0775, true);

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

    protected function tearDown(): void
    {
        $this->removeDir($this->projectDir);
    }

    private function removeDir(string $dir): void
    {
        if (!is_dir($dir)) {
            return;
        }
        foreach (scandir($dir) as $item) {
            if ('.' === $item || '..' === $item) {
                continue;
            }
            $path = $dir.'/'.$item;
            is_dir($path) ? $this->removeDir($path) : unlink($path);
        }
        rmdir($dir);
    }

    private function makePdf(string $title = 'Journal des ventes', string $from = '01/01/2026', string $to = '31/03/2026'): ComptaPdf
    {
        return new ComptaPdf($this->kernel, $title, $from, $to);
    }

    public function testGenerateEmptyDataProducesValidPdf(): void
    {
        $pdf = $this->makePdf();
        $pdf->generate();

        $output = $pdf->Output('S');

        $this->assertStringStartsWith('%PDF', $output);
    }

    public function testGenerateWithRowsProducesValidPdf(): void
    {
        $pdf = $this->makePdf('Journal des ventes');
        $pdf->setData([
            [
                'JournalCode' => 'VTE',
                'JournalLib' => 'Ventes',
                'EcritureNum' => 'E001',
                'EcritureDate' => '01/01/2026',
                'CompteNum' => '411000',
                'CompteLib' => 'Clients',
                'Debit' => '100.00',
                'Credit' => '0.00',
                'EcritureLib' => 'Facture 001',
            ],
            [
                'JournalCode' => 'VTE',
                'JournalLib' => 'Ventes',
                'EcritureNum' => 'E002',
                'EcritureDate' => '15/01/2026',
                'CompteNum' => '706000',
                'CompteLib' => 'Prestations',
                'Debit' => '0.00',
                'Credit' => '100.00',
                'EcritureLib' => 'Facture 001',
            ],
        ]);
        $pdf->generate();

        $output = $pdf->Output('S');

        $this->assertStringStartsWith('%PDF', $output);
    }

    public function testGenerateWithDebitCreditSummarised(): void
    {
        $pdf = $this->makePdf('Grand Livre');
        $pdf->setData([
            ['Debit' => '200.00', 'Credit' => '50.00', 'EcritureLib' => 'Test'],
            ['Debit' => '300.00', 'Credit' => '100.00', 'EcritureLib' => 'Test2'],
        ]);
        $pdf->generate();

        $output = $pdf->Output('S');

        $this->assertStringStartsWith('%PDF', $output);
        // The PDF should contain total text; check binary content for encoded values
        $this->assertGreaterThan(1000, \strlen($output));
    }

    public function testGenerateWithUnknownColumnsDistributesWidth(): void
    {
        $pdf = $this->makePdf('Rapport custom');
        $pdf->setData([
            ['ColA' => 'Valeur1', 'ColB' => 'Valeur2', 'ColC' => '42.00'],
            ['ColA' => 'Valeur3', 'ColB' => 'Valeur4', 'ColC' => '99.00'],
        ]);
        $pdf->generate();

        $output = $pdf->Output('S');

        $this->assertStringStartsWith('%PDF', $output);
    }

    public function testGenerateWithLogoFile(): void
    {
        // Create a minimal valid JPEG file in the project public dir
        // We use a 1x1 white JPEG for testing
        $jpegData = base64_decode('/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACf/EABQQAQAAAAAAAAAAAAAAAAAAAAD/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8AJQAB/9k=');
        file_put_contents($this->projectDir.'/public/logo.jpg', $jpegData);

        $pdf = $this->makePdf();
        $pdf->generate();

        $output = $pdf->Output('S');

        $this->assertStringStartsWith('%PDF', $output);
    }

    public function testSetDataWithEmptyRowsGeneratesNoDataMessage(): void
    {
        $pdf = $this->makePdf();
        $pdf->setData([]);
        $pdf->generate();

        $output = $pdf->Output('S');

        // Should still produce a valid PDF even with empty data
        $this->assertStringStartsWith('%PDF', $output);
    }

    public function testMultiplePagesWithManyRows(): void
    {
        $rows = [];
        for ($i = 1; $i <= 100; ++$i) {
            $rows[] = [
                'JournalCode' => 'VTE',
                'JournalLib' => 'Ventes',
                'EcritureNum' => 'E'.str_pad((string) $i, 4, '0', STR_PAD_LEFT),
                'EcritureDate' => '01/01/2026',
                'CompteNum' => '411000',
                'CompteLib' => 'Clients',
                'Debit' => number_format($i * 10.5, 2),
                'Credit' => '0.00',
                'EcritureLib' => 'Ligne '.$i,
            ];
        }

        $pdf = $this->makePdf('Export FEC');
        $pdf->setData($rows);
        $pdf->generate();

        $output = $pdf->Output('S');

        $this->assertStringStartsWith('%PDF', $output);
    }

    public function testGenerateWithMontantHtColumn(): void
    {
        // Covers the 'MontantHT' totals path in writeSummary (grand livre / balance)
        $pdf = $this->makePdf('Grand Livre');
        $pdf->setData([
            ['MontantHT' => '100.00', 'MontantTVA' => '20.00', 'MontantTTC' => '120.00', 'EcritureLib' => 'Test'],
        ]);
        $pdf->generate();

        $output = $pdf->Output('S');

        $this->assertStringStartsWith('%PDF', $output);
    }

    public function testGenerateWithAllNumericColumns(): void
    {
        // Covers isNumericColumn for: Solde, MontantDevise, Montantdevise, JoursRetard
        $pdf = $this->makePdf('Balance');
        $pdf->setData([
            [
                'Debit' => '500.00',
                'Credit' => '200.00',
                'Solde' => '300.00',
                'MontantDevise' => '300.00',
                'Montantdevise' => '300.00',
                'JoursRetard' => '5',
                'EcritureLib' => 'Test',
            ],
        ]);
        $pdf->generate();

        $output = $pdf->Output('S');

        $this->assertStringStartsWith('%PDF', $output);
    }

    public function testGenerateSignatureBlockNearPageBottom(): void
    {
        // Creates many rows so the signature block needs a new page
        $rows = [];
        for ($i = 1; $i <= 60; ++$i) {
            $rows[] = [
                'JournalCode' => 'VTE',
                'EcritureLib' => 'Ligne '.$i,
                'Debit' => '0.00',
                'Credit' => '0.00',
            ];
        }

        $pdf = $this->makePdf('Grand Livre');
        $pdf->setData($rows);
        $pdf->generate();

        $output = $pdf->Output('S');

        $this->assertStringStartsWith('%PDF', $output);
    }

    public function testWriteDataTablePageBreakTriggered(): void
    {
        // Uses enough rows to trigger the page-break branch inside writeDataTable.
        // In A4 landscape (210mm height), context block ends ~Y=75mm, header row=6mm,
        // page-break threshold = GetPageHeight()-30 = 180mm.
        // After ~19 rows (5mm each) Y exceeds threshold -> AddPage inside writeDataTable.
        $rows = [];
        for ($i = 1; $i <= 22; ++$i) {
            $rows[] = [
                'EcritureNum' => 'E'.str_pad((string) $i, 3, '0', STR_PAD_LEFT),
                'EcritureLib' => 'Ecriture '.$i,
                'Debit' => number_format($i * 5.0, 2),
                'Credit' => '0.00',
            ];
        }

        $pdf = $this->makePdf('Test page break table');
        $pdf->setData($rows);
        $pdf->generate();

        $output = $pdf->Output('S');

        $this->assertStringStartsWith('%PDF', $output);
        // Multiple pages must have been generated
        $this->assertGreaterThan(3000, \strlen($output));
    }

    public function testWriteSignatureBlockPageBreakTriggered(): void
    {
        // Fill the page with just enough rows so that after writeSummary the Y
        // position is too close to the bottom (< 45mm remaining) and writeSignatureBlock
        // adds a new page. With A4 landscape (210mm) and context+header taking ~81mm,
        // 18 rows (5mm each = 90mm) brings Y to ~171mm, then writeSummary adds ~18mm
        // bringing Y to ~189mm. writeSignatureBlock checks Y+45 > 185mm -> new page.
        $rows = [];
        for ($i = 1; $i <= 18; ++$i) {
            $rows[] = [
                'EcritureNum' => 'E'.str_pad((string) $i, 3, '0', STR_PAD_LEFT),
                'EcritureLib' => 'Ecriture '.$i,
                'Debit' => number_format($i * 10.0, 2),
                'Credit' => number_format($i * 2.0, 2),
            ];
        }

        $pdf = $this->makePdf('Test signature page break');
        $pdf->setData($rows);
        $pdf->generate();

        $output = $pdf->Output('S');

        $this->assertStringStartsWith('%PDF', $output);
        $this->assertGreaterThan(2000, \strlen($output));
    }

    public function testHeaderAndFooterCalledExplicitly(): void
    {
        // Explicitly call Header/Footer to ensure method-level coverage
        $pdf = $this->makePdf('Test Header Footer');

        // Header and Footer are public methods, call them directly
        // Note: FPDF requires a page to be added before calling Header in most contexts,
        // but they can be triggered via generate()
        $pdf->generate();

        // Explicitly call Header and Footer to guarantee they are covered
        $pdf->Header();
        $pdf->Footer();

        $output = $pdf->Output('S');
        $this->assertStringStartsWith('%PDF', $output);
    }

    public function testSetDataReturnedColumnsAreUsedInGenerate(): void
    {
        // Verify setData properly sets columns used during generation
        $pdf = $this->makePdf('Test setData');
        $rows = [
            ['CompteNum' => '411000', 'CompteLib' => 'Clients', 'Debit' => '100.00', 'Credit' => '0.00'],
        ];
        $pdf->setData($rows);

        // Call setData again with different data to cover re-assignment path
        $pdf->setData($rows);
        $pdf->generate();

        $output = $pdf->Output('S');
        $this->assertStringStartsWith('%PDF', $output);
    }
}
