<?php

namespace App\Services\Billing;

use App\Models\Invoice;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\View;

/**
 * An invoice as a PDF, built only from what was frozen into the document.
 *
 * Nothing here reads today's settings. Every value — the issuer's address, its
 * VAT number, the rate, the customer's address, the lines — comes from the
 * snapshot taken when the number was assigned. That is what makes it safe to
 * store no PDF at all: rendering the same invoice in 2033 produces the same
 * page it produced on the day it was issued, whatever has changed in between.
 *
 * The one exception is the logo file, which is referenced by path rather than
 * copied. A replaced logo therefore appears on old invoices too — recorded here
 * as a known limitation rather than hidden: keeping every superseded logo
 * forever is a real cost, and a changed mark on a reprint misleads nobody about
 * what was charged.
 */
final class InvoiceRenderer
{
    public function forInvoice(Invoice $invoice): string
    {
        return $this->render((array) $invoice->snapshot);
    }

    /** @param array<string, mixed> $snapshot */
    public function render(array $snapshot): string
    {
        $issuer = (array) ($snapshot['issuer'] ?? []);

        $pdf = new InvoiceDocument('P', 'mm', 'A4', true, 'UTF-8');
        $pdf->setIssuer($issuer, $this->logoFile($issuer), __('invoice.page'), __('invoice.issuer_vat'));

        $pdf->SetCreator('CluPilot');
        $pdf->SetAuthor((string) ($issuer['name'] ?? ''));
        $pdf->SetTitle((string) ($snapshot['meta']['number'] ?? ''));

        // Room at the bottom for the four-column footer, which is fixed height
        // and would otherwise be written over the last line of the table.
        $pdf->SetMargins(20, 34, 20);
        $pdf->SetAutoPageBreak(true, 34);
        $pdf->SetFont('dejavusans', '', 9);

        $pdf->AddPage();

        // Rendered from a Blade template: a table of line items expressed as
        // Cell() calls with millimetre coordinates is a table nobody will ever
        // adjust, and this one has to be adjusted the first time somebody sees
        // it on paper.
        $pdf->writeHTML(
            View::make('pdf.invoice', ['s' => $snapshot])->render(),
            true, false, true, false, ''
        );

        return $pdf->Output('', 'S');
    }

    /**
     * The logo on disk, or nothing.
     *
     * Nothing is a perfectly good answer: an invoice without a logo is still a
     * valid invoice, and refusing to render one because an image is missing
     * would block the only document that actually has to exist.
     *
     * @param  array<string, mixed>  $issuer
     */
    private function logoFile(array $issuer): string
    {
        $path = trim((string) ($issuer['logo_path'] ?? ''));

        if ($path === '') {
            return '';
        }

        $full = Storage::disk('public')->path($path);

        return is_file($full) ? $full : '';
    }
}
