<?php
class PdfGenerator
{
    public function generateInvoicePdf(array $invoice, array $items, array $siteSettings, string $logoPath = ''): string
    {
        $header = $this->prepareHeader($invoice, $siteSettings);
        $body = $this->prepareBody($invoice, $items);

        $pdfText = implode("\n", array_merge([
            'BT',
            '/F1 18 Tf 50 780 Td',
            '<' . $this->toPdfHex($header['company_name']) . '> Tj',
            'T*',
            '<' . $this->toPdfHex($header['company_address']) . '> Tj',
            'ET',

            'BT',
            '/F1 14 Tf 350 780 Td',
            '<' . $this->toPdfHex('Invoice') . '> Tj',
            'T*',
            '<' . $this->toPdfHex('Invoice #: ' . $header['invoice_number']) . '> Tj',
            'T*',
            '<' . $this->toPdfHex('Date: ' . $header['invoice_date']) . '> Tj',
            'T*',
            '<' . $this->toPdfHex('Due: ' . $header['due_date']) . '> Tj',
            'T*',
            '<' . $this->toPdfHex('Status: ' . $header['status']) . '> Tj',
            'ET',

            'BT',
            '/F1 10 Tf 50 700 Td',
            '<' . $this->toPdfHex('Bill To:') . '> Tj',
            'T*',
            '<' . $this->toPdfHex($header['customer_name']) . '> Tj',
            'ET',

            'BT',
            '/F1 10 Tf 50 672 Td',
            '<' . $this->toPdfHex('Contact:') . '> Tj',
            'T*',
            '<' . $this->toPdfHex($header['contact_email']) . '> Tj',
            'T*',
            '<' . $this->toPdfHex($header['contact_phone']) . '> Tj',
            'T*',
            '<' . $this->toPdfHex($header['contact_location']) . '> Tj',
            'ET',

            'BT',
            '/F1 12 Tf 50 600 Td',
            '<' . $this->toPdfHex('Description') . '> Tj',
            '340 0 Td',
            '<' . $this->toPdfHex('Qty') . '> Tj',
            '430 0 Td',
            '<' . $this->toPdfHex('Unit') . '> Tj',
            '490 0 Td',
            '<' . $this->toPdfHex('Total') . '> Tj',
            'ET',
        ], $body));

        $fontStream = $this->getFontStream();
        $contentStream = $this->wrapStream($pdfText);

        $objects = [];
        $objects[] = "1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj";
        $objects[] = "2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj";
        $objects[] = "3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >>\nendobj";

        if ($fontStream !== '') {
            $objects[] = "4 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /Identity-H /DescendantFonts [6 0 R] /ToUnicode 7 0 R >>\nendobj";
            $objects[] = "6 0 obj\n<< /Type /Font /Subtype /CIDFontType2 /BaseFont /Helvetica /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor 8 0 R /DW 1000 >>\nendobj";
            $objects[] = "7 0 obj\n<< /Length 45 >>\nstream\n/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> def\n/CMapName /Identity-H def\n1 begincodespacerange\n<0000> <FFFF>\nendcodespacerange\n1 beginbfrange\n<0000> <FFFF> <0000>\nendbfrange\nendcmap\nCMapName currentdict /CMap defineresource pop\nend\nend\nendstream\nendobj";
            $objects[] = "8 0 obj\n<< /Type /FontDescriptor /FontName /Helvetica /Flags 4 /ItalicAngle 0 /Ascent 718 /Descent -207 /CapHeight 718 /StemV 70 /FontBBox [0 -207 584 718] /FontFile2 9 0 R >>\nendobj";
            $fontObj = $this->wrapStream($fontStream);
            $objects[] = "9 0 obj\n{$fontObj}\nendobj";
        } else {
            $objects[] = "4 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj";
        }

        $objects[] = "5 0 obj\n{$contentStream}\nendobj";

        $pdf = "%PDF-1.4\n%\xe2\xe3\xcf\xd3\n";
        $offsets = [];
        $position = strlen($pdf);
        foreach ($objects as $object) {
            $offsets[] = $position;
            $pdf .= $object . "\n";
            $position = strlen($pdf);
        }

        $xrefPosition = strlen($pdf);
        $pdf .= "xref\n0 " . (count($objects) + 1) . "\n0000000000 65535 f \n";
        foreach ($offsets as $offset) {
            $pdf .= sprintf('%010d 00000 n ', $offset);
        }

        $pdf .= "\ntrailer\n<< /Size " . (count($objects) + 1) . " /Root 1 0 R >>\nstartxref\n{$xrefPosition}\n%%EOF";
        return $pdf;
    }

    private function prepareHeader(array $invoice, array $siteSettings): array
    {
        return [
            'company_name' => 'Balorp Ventures',
            'company_address' => $siteSettings['topbar_location'] ?? 'Accra, Ghana',
            'invoice_number' => $invoice['invoice_number'] ?? 'N/A',
            'invoice_date' => $invoice['created_at'] ?? date('Y-m-d'),
            'due_date' => $invoice['due_date'] ?? date('Y-m-d'),
            'status' => $invoice['status'] ?? 'Outstanding',
            'customer_name' => $invoice['customer_name'] ?? 'Customer',
            'contact_email' => $siteSettings['topbar_email'] ?? 'info@balorpfinishes.com',
            'contact_phone' => $siteSettings['topbar_phone'] ?? '+233 24 000 0000',
            'contact_location' => $siteSettings['topbar_location'] ?? 'Accra, Ghana',
        ];
    }

    private function prepareBody(array $invoice, array $items): array
    {
        $lines = [];
        $y = 560;
        foreach ($items as $item) {
            $description = $item['description'] ?? '';
            if (empty(trim((string)$description))) {
                continue;
            }
            $quantity = (int)($item['quantity'] ?? 0);
            $unitPrice = number_format((float)($item['unit_price'] ?? 0), 2);
            $totalPrice = number_format((float)($item['total_price'] ?? ($quantity * (float)$item['unit_price'])), 2);

            $lines[] = 'BT';
            $lines[] = '/F1 10 Tf 50 ' . $y . ' Td';
            $lines[] = '<' . $this->toPdfHex($description) . '> Tj';
            $lines[] = '340 0 Td';
            $lines[] = '<' . $this->toPdfHex((string)$quantity) . '> Tj';
            $lines[] = '90 0 Td';
            $lines[] = '<' . $this->toPdfHex('GHS ' . $unitPrice) . '> Tj';
            $lines[] = '70 0 Td';
            $lines[] = '<' . $this->toPdfHex('GHS ' . $totalPrice) . '> Tj';
            $lines[] = 'ET';
            $y -= 18;
        }

        $subtotal = number_format((float)($invoice['total_amount'] ?? 0), 2);
        $paid = number_format((float)($invoice['paid_amount'] ?? 0), 2);
        $due = number_format((float)((float)$invoice['total_amount'] - (float)$invoice['paid_amount']), 2);

        $lines[] = 'BT';
        $lines[] = '/F1 12 Tf 350 ' . max(100, $y - 20) . ' Td';
        $lines[] = '<' . $this->toPdfHex('Subtotal:') . '> Tj';
        $lines[] = '140 0 Td';
        $lines[] = '<' . $this->toPdfHex('GHS ' . $subtotal) . '> Tj';
        $lines[] = 'ET';

        $lines[] = 'BT';
        $lines[] = '/F1 12 Tf 350 ' . max(80, $y - 40) . ' Td';
        $lines[] = '<' . $this->toPdfHex('Paid:') . '> Tj';
        $lines[] = '140 0 Td';
        $lines[] = '<' . $this->toPdfHex('GHS ' . $paid) . '> Tj';
        $lines[] = 'ET';

        $lines[] = 'BT';
        $lines[] = '/F1 12 Tf 350 ' . max(60, $y - 60) . ' Td';
        $lines[] = '<' . $this->toPdfHex('Outstanding:') . '> Tj';
        $lines[] = '140 0 Td';
        $lines[] = '<' . $this->toPdfHex('GHS ' . $due) . '> Tj';
        $lines[] = 'ET';

        return $lines;
    }

    private function getFontStream(): string
    {
        $paths = [
            'C:/Windows/Fonts/arial.ttf',
            'C:/Windows/Fonts/liberation-sans.ttf',
            'C:/Windows/Fonts/calibri.ttf',
            '/Library/Fonts/Arial.ttf',
            '/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf',
        ];

        foreach ($paths as $path) {
            if (file_exists($path)) {
                return file_get_contents($path);
            }
        }

        return '';
    }

    private function wrapStream(string $content): string
    {
        return "<< /Length " . strlen($content) . " >>\nstream\n{$content}\nendstream";
    }

    private function toPdfHex(string $text): string
    {
        $utf8 = mb_convert_encoding($text, 'UTF-16BE', 'UTF-8');
        $bom = "\xFE\xFF";
        $data = $bom . $utf8;
        return strtoupper(bin2hex($data));
    }
}
