<?php

namespace App\Support\Pdf;

/**
 * Generador PDF súper simple (texto) sin dependencias externas.
 *
 * - Soporta múltiples páginas.
 * - Usa fuente core (Courier) incluida en cualquier lector PDF.
 * - Ideal para reportes tipo auditoría.
 */
class SimplePdf
{
    /** @var array<int, array<int, string>> */
    private array $pages = [];

    private string $orientation;

    /**
     * @param 'P'|'L' $orientation
     */
    public function __construct(string $orientation = 'P')
    {
        $orientation = strtoupper($orientation);
        $this->orientation = in_array($orientation, ['P', 'L'], true) ? $orientation : 'P';
    }

    /**
     * @param array<int, string> $lines
     */
    public function addPage(array $lines): void
    {
        $this->pages[] = array_values($lines);
    }

    public function output(): string
    {
        // A4 en puntos (pt): 595x842
        $w = $this->orientation === 'L' ? 842 : 595;
        $h = $this->orientation === 'L' ? 595 : 842;

        $objects = [];
        $offsets = [];

        $addObj = function (string $body) use (&$objects): int {
            $objects[] = $body;
            return count($objects);
        };

        $pdf = "%PDF-1.4\n";

        // 1) Catalog
        $catalogId = $addObj("<< /Type /Catalog /Pages 2 0 R >>");

        // 2) Pages (se completa luego)
        $pagesId = $addObj('');

        // 3) Font (Courier)
        $fontId = $addObj("<< /Type /Font /Subtype /Type1 /BaseFont /Courier >>");

        $pageObjectIds = [];

        foreach ($this->pages as $pageLines) {
            $contentStream = $this->buildContentStream($pageLines, $w, $h);
            $contentId = $addObj($contentStream);

            $pageBody = "<<\n".
                "/Type /Page\n".
                "/Parent {$pagesId} 0 R\n".
                "/MediaBox [0 0 {$w} {$h}]\n".
                "/Resources << /Font << /F1 {$fontId} 0 R >> >>\n".
                "/Contents {$contentId} 0 R\n".
                ">>";

            $pageId = $addObj($pageBody);
            $pageObjectIds[] = $pageId;
        }

        $kids = implode(' ', array_map(fn ($id) => $id.' 0 R', $pageObjectIds));
        $pagesBody = "<< /Type /Pages /Kids [ {$kids} ] /Count ".count($pageObjectIds)." >>";
        $objects[$pagesId - 1] = $pagesBody;

        // Escribe objetos y registra offsets
        foreach ($objects as $i => $body) {
            $objNum = $i + 1;
            $offsets[$objNum] = strlen($pdf);
            $pdf .= "{$objNum} 0 obj\n{$body}\nendobj\n";
        }

        // xref
        $xrefPos = strlen($pdf);
        $pdf .= "xref\n";
        $pdf .= "0 ".(count($objects) + 1)."\n";
        $pdf .= "0000000000 65535 f \n";
        for ($i = 1; $i <= count($objects); $i++) {
            $pdf .= str_pad((string) $offsets[$i], 10, '0', STR_PAD_LEFT)." 00000 n \n";
        }

        // trailer
        $pdf .= "trailer\n";
        $pdf .= "<< /Size ".(count($objects) + 1)." /Root {$catalogId} 0 R >>\n";
        $pdf .= "startxref\n{$xrefPos}\n%%EOF";

        return $pdf;
    }

    /**
     * @param array<int, string> $lines
     */
    private function buildContentStream(array $lines, int $w, int $h): string
    {
        $fontSize = 9;
        $lineHeight = 11;
        $left = 36; // 0.5in
        $top = $h - 36;

        $content = "BT\n/F1 {$fontSize} Tf\n";
        $content .= $left." ".$top." Td\n";

        $first = true;
        foreach ($lines as $line) {
            $line = $this->escapePdfText($line);
            if (!$first) {
                $content .= "0 -{$lineHeight} Td\n";
            }
            $first = false;
            $content .= "({$line}) Tj\n";
        }

        $content .= "ET\n";

        $len = strlen($content);
        return "<< /Length {$len} >>\nstream\n{$content}endstream";
    }

    private function escapePdfText(string $text): string
    {
        // PDF literal strings requieren escapar: \ ( )
        $text = str_replace('\\', '\\\\', $text);
        $text = str_replace('(', '\\(', $text);
        $text = str_replace(')', '\\)', $text);

        // Evita caracteres de control
        $text = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F]/u', '', $text) ?? $text;
        return $text;
    }
}
