"""Invoice PDFs.

The PDF is rendered on demand from the Due's frozen snapshot (plan, amount, dates), so it
carries no state of its own beyond the number. Only the number is stored — an accountant
reconciles against it, so it is allocated once, never recomputed.
"""

from django.template.loader import render_to_string

from billing.models import Due, Invoice
from billing.services import BillingError


def issue_invoice(due: Due) -> Invoice:
    """One invoice per due, allocated once. Re-issuing returns the existing one rather than
    burning a number — a gap in an invoice series is a question you do not want to answer."""
    invoice, _created = Invoice.objects.get_or_create(due=due)

    return invoice


def render_pdf(html: str) -> bytes:
    """HTML to PDF.

    WeasyPrint is imported here, not at module scope: it binds to native pango/cairo
    libraries, and a machine without them must still be able to run the app, the tests and
    every other page — it should only fail when someone actually asks for a PDF, and say why.
    """
    try:
        from weasyprint import HTML
    except (ImportError, OSError) as error:
        raise BillingError("PDF rendering needs the native pango/cairo libraries (on macOS: brew install pango).") from error

    return HTML(string=html).write_pdf()


def invoice_pdf(invoice: Invoice, base_url: str | None = None) -> bytes:
    html = render_to_string("billing/invoice.html", {"invoice": invoice, "due": invoice.due, "club": invoice.due.club, "payments": invoice.due.payments.all()})

    return render_pdf(html)
