<?php

declare(strict_types=1);

namespace FatBottom\Services;

final class MenuPdfExporter
{
    public function output(string $businessName, array $categories): never
    {
        $pages = [];
        $lines = [];
        foreach ($categories as $category) {
            $lines[] = ['type' => 'category', 'text' => strtoupper((string) $category['name'])];
            foreach ($category['items'] as $item) {
                $lines[] = [
                    'type' => 'item',
                    'text' => sprintf('%s  $%.2f', $item['name'], $item['price_cents'] / 100),
                ];
                $lines[] = ['type' => 'description', 'text' => (string) $item['description']];
            }
        }

        foreach (array_chunk($lines, 24) as $chunk) {
            $pages[] = $this->pageStream($businessName, $chunk);
        }
        if ($pages === []) {
            $pages[] = $this->pageStream($businessName, []);
        }

        $pdf = $this->buildPdf($pages);
        header('Content-Type: application/pdf');
        header('Content-Disposition: attachment; filename="fat-bottom-grille-menu.pdf"');
        header('Content-Length: ' . strlen($pdf));
        echo $pdf;
        exit;
    }

    private function pageStream(string $businessName, array $lines): string
    {
        $escape = static function (string $value): string {
            $ascii = function_exists('iconv')
                ? (iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $value) ?: $value)
                : preg_replace('/[^\x20-\x7E]/', '', $value);

            return str_replace(
                ['\\', '(', ')', "\r", "\n"],
                ['\\\\', '\\(', '\\)', ' ', ' '],
                (string) $ascii
            );
        };

        $stream = "0.035 0.071 0.102 rg\n0 0 612 792 re f\n";
        $stream .= "0.78 0.58 0.20 rg\nBT /F1 25 Tf 46 738 Td (" . $escape($businessName) . ") Tj ET\n";
        $stream .= "0.91 0.92 0.90 rg\nBT /F1 10 Tf 46 718 Td (KEYSER, WEST VIRGINIA  |  ONLINE MENU) Tj ET\n";
        $stream .= "0.78 0.58 0.20 RG\n46 704 m 566 704 l S\n";
        $y = 678;
        foreach ($lines as $line) {
            $text = $escape(substr((string) $line['text'], 0, 92));
            if ($line['type'] === 'category') {
                $stream .= "0.78 0.58 0.20 rg\nBT /F1 15 Tf 46 {$y} Td ({$text}) Tj ET\n";
                $y -= 24;
            } elseif ($line['type'] === 'item') {
                $stream .= "0.96 0.95 0.89 rg\nBT /F1 12 Tf 54 {$y} Td ({$text}) Tj ET\n";
                $y -= 17;
            } else {
                $stream .= "0.68 0.72 0.72 rg\nBT /F1 8 Tf 54 {$y} Td ({$text}) Tj ET\n";
                $y -= 23;
            }
        }
        $stream .= "0.55 0.60 0.62 rg\nBT /F1 8 Tf 46 35 Td (Prices and availability are subject to change.) Tj ET\n";
        return $stream;
    }

    private function buildPdf(array $streams): string
    {
        $objects = [];
        $objects[1] = '<< /Type /Catalog /Pages 2 0 R >>';
        $pageIds = [];
        $nextId = 4;
        foreach ($streams as $index => $stream) {
            $pageId = $nextId++;
            $contentId = $nextId++;
            $pageIds[] = $pageId . ' 0 R';
            $objects[$pageId] = sprintf(
                '<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Resources << /Font << /F1 3 0 R >> >> /Contents %d 0 R >>',
                $contentId
            );
            $objects[$contentId] = "<< /Length " . strlen($stream) . " >>\nstream\n{$stream}endstream";
        }
        $objects[2] = '<< /Type /Pages /Kids [' . implode(' ', $pageIds) . '] /Count ' . count($pageIds) . ' >>';
        $objects[3] = '<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>';
        ksort($objects);

        $pdf = "%PDF-1.4\n";
        $offsets = [0];
        foreach ($objects as $id => $object) {
            $offsets[$id] = strlen($pdf);
            $pdf .= "{$id} 0 obj\n{$object}\nendobj\n";
        }
        $xref = strlen($pdf);
        $count = max(array_keys($objects)) + 1;
        $pdf .= "xref\n0 {$count}\n0000000000 65535 f \n";
        for ($id = 1; $id < $count; $id++) {
            $pdf .= sprintf("%010d 00000 n \n", $offsets[$id] ?? 0);
        }
        $pdf .= "trailer\n<< /Size {$count} /Root 1 0 R >>\nstartxref\n{$xref}\n%%EOF";
        return $pdf;
    }
}
