<?php

namespace App\Core;

class SimplePdf
{
    /**
     * @param string $title
     * @param array<int,array<string,string>> $rows Each row is an ordered
     *        [label => formatted value] array, e.g. from
     *        KpiFieldLabels::toLabeledRow(). All rows must share the same
     *        keys/order so section grouping lines up consistently.
     */
    public static function tableReport(string $title, array $rows): string
    {
        $pageWidth = 595;
        $pageHeight = 842;
        $marginX = 40;
        $lineHeight = 13;
        $usableBottom = 55;

        // Reverse-lookup: label => group name, so we can print section
        // headers in the same grouping used on the entry form / detail page.
        $labelToGroup = [];
        foreach (KpiFieldLabels::groups() as $groupName => $fields) {
            foreach ($fields as $label) {
                $labelToGroup[$label] = $groupName;
            }
        }

        $pagesContent = [];
        $stream = '';
        $y = $pageHeight - 50;

        $startPage = function () use (&$stream, &$y, $pageHeight, $marginX, $pageWidth, $title) {
            $stream = '';
            $y = $pageHeight - 50;
            $stream .= self::text('HAMMERHEAD STAFFING', $marginX, $y, 'F2', 9);
            $stream .= self::text(date('M j, Y g:i A'), $pageWidth - $marginX - 130, $y, 'F1', 8);
            $y -= 18;
            $stream .= self::line($marginX, $y, $pageWidth - $marginX, $y, 0.75);
            $y -= 22;
            $stream .= self::text($title, $marginX, $y, 'F2', 15);
            $y -= 26;
        };

        $startPage();

        if (empty($rows)) {
            $stream .= self::text('No records match the selected filters.', $marginX, $y, 'F1', 10);
            $pagesContent[] = $stream;
            return self::assemble($pagesContent, $pageWidth, $pageHeight);
        }

        foreach ($rows as $recordIndex => $row) {
            $recordLabel = 'Record ' . ($recordIndex + 1);
            if (isset($row['Submitted By'], $row['Week Ending'])) {
                $recordLabel = $row['Submitted By'] . '  —  Week Ending ' . $row['Week Ending'];
            }

            if ($y < $usableBottom + 50) {
                $pagesContent[] = $stream;
                $startPage();
            }

            // Record title bar
            $stream .= self::rect($marginX, $y - 4, $pageWidth - 2 * $marginX, 20, 0.93);
            $stream .= self::text($recordLabel, $marginX + 8, $y + 2, 'F2', 11);
            $y -= 26;

            $currentGroup = null;
            foreach ($row as $label => $val) {
                $group = $labelToGroup[$label] ?? null;

                if ($group !== null && $group !== $currentGroup) {
                    if ($y < $usableBottom + 30) {
                        $pagesContent[] = $stream;
                        $startPage();
                    }
                    $currentGroup = $group;
                    $y -= 4;
                    $stream .= self::text(strtoupper($group), $marginX, $y, 'F2', 8.5);
                    $y -= 13;
                }

                if ($y < $usableBottom) {
                    $pagesContent[] = $stream;
                    $startPage();
                }

                $text = self::truncate($label . ':  ' . (string)$val, 70);
                $stream .= self::text($text, $marginX + 6, $y, 'F1', 8.5);
                $y -= $lineHeight;
            }

            $y -= 16;
        }

        $pagesContent[] = $stream;
        return self::assemble($pagesContent, $pageWidth, $pageHeight);
    }

    private static function truncate(string $s, int $len): string
    {
        return strlen($s) > $len ? substr($s, 0, $len - 1) . '...' : $s;
    }

    private static function text(string $text, float $x, float $y, string $font, float $size): string
    {
        return "BT /{$font} {$size} Tf {$x} {$y} Td (" . self::esc($text) . ") Tj ET\n";
    }

    private static function line(float $x1, float $y1, float $x2, float $y2, float $width = 1.0): string
    {
        return "{$width} w {$x1} {$y1} m {$x2} {$y2} l S\n";
    }

    private static function rect(float $x, float $y, float $w, float $h, float $gray): string
    {
        // Light gray filled rectangle used for record title bars.
        return "{$gray} g {$x} {$y} {$w} {$h} re f 0 g\n";
    }

    private static function esc(string $s): string
    {
        return str_replace(['\\', '(', ')'], ['\\\\', '\\(', '\\)'], $s);
    }

    private static function assemble(array $pageStreams, float $w, float $h): string
    {
        $objects = [];
        $objects[1] = "1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n";

        $kids = [];
        $objNum = 3;
        $pageObjNums = [];
        foreach ($pageStreams as $i => $stream) {
            $pageObjNums[$i] = $objNum;
            $objNum += 2;
        }
        $fontObjNum = $objNum;
        $fontBoldObjNum = $objNum + 1;

        $totalPages = count($pageStreams);
        foreach ($pageStreams as $i => $stream) {
            // Footer: page number, appended to every page's content stream.
            $stream .= self::text('Page ' . ($i + 1) . ' of ' . $totalPages, $w - 100, 30, 'F1', 8);

            $pageNum = $pageObjNums[$i];
            $contentNum = $pageNum + 1;
            $kids[] = "{$pageNum} 0 R";

            $objects[$pageNum] = "{$pageNum} 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 {$w} {$h}] " .
                "/Resources << /Font << /F1 {$fontObjNum} 0 R /F2 {$fontBoldObjNum} 0 R >> >> /Contents {$contentNum} 0 R >>\nendobj\n";

            $len = strlen($stream);
            $objects[$contentNum] = "{$contentNum} 0 obj\n<< /Length {$len} >>\nstream\n{$stream}endstream\nendobj\n";
        }

        $objects[2] = "2 0 obj\n<< /Type /Pages /Kids [" . implode(' ', $kids) . "] /Count " . count($kids) . " >>\nendobj\n";
        $objects[$fontObjNum] = "{$fontObjNum} 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n";
        $objects[$fontBoldObjNum] = "{$fontBoldObjNum} 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold >>\nendobj\n";

        ksort($objects);

        $pdf = "%PDF-1.4\n";
        $offsets = [0 => 0];
        foreach ($objects as $num => $body) {
            $offsets[$num] = strlen($pdf);
            $pdf .= $body;
        }

        $xrefPos = strlen($pdf);
        $maxNum = max(array_keys($objects));
        $pdf .= "xref\n0 " . ($maxNum + 1) . "\n";
        $pdf .= "0000000000 65535 f \n";
        for ($n = 1; $n <= $maxNum; $n++) {
            if (isset($objects[$n])) {
                $pdf .= sprintf("%010d 00000 n \n", $offsets[$n]);
            } else {
                $pdf .= "0000000000 00000 f \n";
            }
        }
        $pdf .= "trailer\n<< /Size " . ($maxNum + 1) . " /Root 1 0 R >>\nstartxref\n{$xrefPos}\n%%EOF";

        return $pdf;
    }
}
