/**
 * Invoice PDF rendering via @react-pdf/renderer.
 *
 * Design notes:
 *
 * - The template is a React component (JSX). Visual tweaks happen
 *   here — colors, fonts, spacing, layout. To swap branding later,
 *   edit BRAND_* constants below or replace the logo component.
 *
 * - All strings are pulled from MESSAGES[locale]. To add a new
 *   language, copy the German block and translate. Locale is
 *   frozen on the invoice at issue time (invoices.locale column);
 *   re-rendering a historical invoice always uses the same locale.
 *
 * - The logo is inlined as React-PDF SVG primitives so no asset
 *   loading or font-bundle wrangling is needed. It travels with
 *   the code.
 *
 * - VAT note (reverse charge etc.) is appended below the totals
 *   block. Notes are localized in the same MESSAGES map.
 *
 * - QR-bill (Swiss bank transfer) is intentionally NOT included
 *   in v1 — it lands in Phase 7. We render plain bank instructions
 *   as text.
 */

import React from "react";
import {
  Document,
  Page,
  Text,
  View,
  StyleSheet,
  Svg,
  Polygon,
  Polyline,
  renderToBuffer,
} from "@react-pdf/renderer";
import type { Invoice, InvoiceLine, InvoiceLineKind } from "@/types";

// ---------------------------------------------------------------------------
// Brand constants — edit here to tweak look without touching layout
// ---------------------------------------------------------------------------

const BRAND = {
  name: "PieCed IT",
  // Primary emerald — matches the logo SVG fill (#10B981).
  primary: "#10B981",
  // Slightly darker emerald for headings.
  primaryDark: "#0a8060",
  textColor: "#1a1a1a",
  mutedColor: "#666",
  borderColor: "#d4d4d4",
  // Issuer block — change these to your real legal info.
  issuer: {
    legalName: "PieCed IT",
    addressLine1: "Cedric Mosimann",
    addressLine2: "[Strasse Nr.]",
    postalCity: "[PLZ] Basel",
    country: "Switzerland",
    email: "billing@pieced.ch",
    web: "pieced.ch",
    // Show "MWST-Nr. ..." on PDF when set.
    vatNumber: null as string | null,
    // Bank instructions — Phase 7 replaces with QR-bill.
    bankName: "[Bank name]",
    bankIban: "[CHxx xxxx xxxx xxxx xxxx x]",
    bankBic: "[BIC]",
  },
};

// ---------------------------------------------------------------------------
// Localized strings
// ---------------------------------------------------------------------------

interface PdfStrings {
  invoice: string;
  invoiceNumber: string;
  issueDate: string;
  dueDate: string;
  period: string;
  billTo: string;
  // Phase 6 fix: prefix shown before the optional contact-person
  // name on the bill-to block. "z.Hd." (DE) / "Attn:" (EN) /
  // "À l'attention de" (FR) / "c.a." (IT). Empty/unused when the
  // invoice has no contactName on its snapshot.
  attentionPrefix: string;
  description: string;
  quantity: string;
  unitPrice: string;
  amount: string;
  subtotal: string;
  vat: string;
  total: string;
  paymentInstructions: string;
  paymentRefHint: string;
  thankYou: string;
  page: string;
  of: string;
  // Per-line-kind labels (used as section headers)
  kindLabels: Record<InvoiceLineKind, string>;
  // VAT compliance notes
  reverseCharge: string;
  exportNote: string;
}

const MESSAGES: Record<string, PdfStrings> = {
  de: {
    invoice: "Rechnung",
    invoiceNumber: "Rechnungs-Nr.",
    issueDate: "Rechnungsdatum",
    dueDate: "Zahlbar bis",
    period: "Abrechnungsperiode",
    billTo: "Rechnungsempfänger",
    attentionPrefix: "z.Hd.",
    description: "Beschreibung",
    quantity: "Menge",
    unitPrice: "Einzelpreis",
    amount: "Betrag",
    subtotal: "Zwischensumme",
    vat: "MWST",
    total: "Total",
    paymentInstructions: "Zahlungsinformationen",
    paymentRefHint: "Bitte verwenden Sie die Rechnungsnummer als Referenz.",
    thankYou: "Vielen Dank für Ihr Vertrauen.",
    page: "Seite",
    of: "von",
    kindLabels: {
      tenant_monthly: "Monatliche Grundgebühr",
      tenant_setup: "Einrichtungsgebühr",
      ai_usage: "KI-Nutzung",
      threema_messages: "Threema-Nachrichten",
      skill_usage: "Skill-Nutzung",
      skill_setup: "Einrichtungsgebühr Skill",
      adjustment: "Anpassung",
    },
    reverseCharge:
      "Steuerschuldnerschaft des Leistungsempfängers (Reverse Charge).",
    exportNote: "Dienstleistungsexport — keine MWST in Rechnung gestellt.",
  },
  en: {
    invoice: "Invoice",
    invoiceNumber: "Invoice no.",
    issueDate: "Issue date",
    dueDate: "Due date",
    period: "Billing period",
    billTo: "Bill to",
    attentionPrefix: "Attn:",
    description: "Description",
    quantity: "Qty",
    unitPrice: "Unit price",
    amount: "Amount",
    subtotal: "Subtotal",
    vat: "VAT",
    total: "Total",
    paymentInstructions: "Payment instructions",
    paymentRefHint: "Please use the invoice number as the payment reference.",
    thankYou: "Thank you for your business.",
    page: "Page",
    of: "of",
    kindLabels: {
      tenant_monthly: "Monthly fee",
      tenant_setup: "Setup fee",
      ai_usage: "AI usage",
      threema_messages: "Threema messages",
      skill_usage: "Skill usage",
      skill_setup: "Skill setup fee",
      adjustment: "Adjustment",
    },
    reverseCharge:
      "Reverse charge — VAT to be accounted for by the recipient.",
    exportNote: "Export of services — VAT not applicable.",
  },
  fr: {
    invoice: "Facture",
    invoiceNumber: "N° facture",
    issueDate: "Date d'émission",
    dueDate: "Échéance",
    period: "Période de facturation",
    billTo: "Destinataire",
    attentionPrefix: "À l'attention de",
    description: "Description",
    quantity: "Qté",
    unitPrice: "Prix unitaire",
    amount: "Montant",
    subtotal: "Sous-total",
    vat: "TVA",
    total: "Total",
    paymentInstructions: "Informations de paiement",
    paymentRefHint: "Veuillez utiliser le n° de facture comme référence.",
    thankYou: "Merci de votre confiance.",
    page: "Page",
    of: "sur",
    kindLabels: {
      tenant_monthly: "Forfait mensuel",
      tenant_setup: "Frais de configuration",
      ai_usage: "Utilisation IA",
      threema_messages: "Messages Threema",
      skill_usage: "Utilisation Skill",
      skill_setup: "Frais de configuration skill",
      adjustment: "Ajustement",
    },
    reverseCharge:
      "Autoliquidation — TVA à acquitter par le destinataire.",
    exportNote: "Exportation de services — TVA non applicable.",
  },
  it: {
    invoice: "Fattura",
    invoiceNumber: "N. fattura",
    issueDate: "Data di emissione",
    dueDate: "Scadenza",
    period: "Periodo di fatturazione",
    billTo: "Destinatario",
    attentionPrefix: "c.a.",
    description: "Descrizione",
    quantity: "Qtà",
    unitPrice: "Prezzo unitario",
    amount: "Importo",
    subtotal: "Subtotale",
    vat: "IVA",
    total: "Totale",
    paymentInstructions: "Istruzioni di pagamento",
    paymentRefHint: "Si prega di utilizzare il n. di fattura come riferimento.",
    thankYou: "Grazie per la fiducia.",
    page: "Pagina",
    of: "di",
    kindLabels: {
      tenant_monthly: "Canone mensile",
      tenant_setup: "Spese di attivazione",
      ai_usage: "Utilizzo IA",
      threema_messages: "Messaggi Threema",
      skill_usage: "Utilizzo Skill",
      skill_setup: "Spese di attivazione skill",
      adjustment: "Rettifica",
    },
    reverseCharge:
      "Inversione contabile — IVA a carico del destinatario.",
    exportNote: "Esportazione di servizi — IVA non applicabile.",
  },
};

function getStrings(locale: string): PdfStrings {
  return MESSAGES[locale] ?? MESSAGES.de;
}

// ---------------------------------------------------------------------------
// Stylesheet
// ---------------------------------------------------------------------------

const styles = StyleSheet.create({
  page: {
    paddingTop: 40,
    paddingBottom: 60,
    paddingHorizontal: 40,
    fontSize: 9,
    color: BRAND.textColor,
    lineHeight: 1.4,
  },
  headerRow: {
    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "flex-start",
    marginBottom: 28,
  },
  logoWrap: { width: 60, height: 90 },
  issuerBlock: { textAlign: "right", fontSize: 8.5, color: BRAND.mutedColor },
  issuerName: { fontSize: 11, color: BRAND.primaryDark, marginBottom: 2 },
  invoiceTitle: { fontSize: 22, color: BRAND.primaryDark, marginBottom: 8 },
  metaTable: {
    flexDirection: "row",
    justifyContent: "space-between",
    marginBottom: 20,
  },
  metaCol: { flexGrow: 1, marginRight: 16 },
  metaLabel: { color: BRAND.mutedColor, fontSize: 8, marginBottom: 2 },
  metaValue: { fontSize: 10, marginBottom: 6 },
  billToBlock: {
    marginBottom: 24,
    padding: 10,
    backgroundColor: "#f7f7f5",
    borderLeftWidth: 3,
    borderLeftColor: BRAND.primary,
  },
  billToLabel: { fontSize: 8, color: BRAND.mutedColor, marginBottom: 4 },
  billToName: { fontSize: 11, marginBottom: 2 },
  table: { marginBottom: 14 },
  tableHeader: {
    flexDirection: "row",
    backgroundColor: BRAND.primaryDark,
    color: "#ffffff",
    paddingVertical: 5,
    paddingHorizontal: 6,
    fontSize: 8.5,
  },
  tableRow: {
    flexDirection: "row",
    borderBottomWidth: 0.5,
    borderBottomColor: BRAND.borderColor,
    paddingVertical: 5,
    paddingHorizontal: 6,
  },
  // Column widths (sum ≈ 100%)
  colDesc: { width: "52%" },
  colQty: { width: "12%", textAlign: "right" },
  colUnit: { width: "16%", textAlign: "right" },
  colAmt: { width: "20%", textAlign: "right" },
  totalsBlock: {
    alignSelf: "flex-end",
    width: "45%",
    marginTop: 8,
  },
  totalsRow: {
    flexDirection: "row",
    justifyContent: "space-between",
    paddingVertical: 3,
  },
  totalsLabel: { color: BRAND.mutedColor },
  totalsValue: { textAlign: "right" },
  totalsGrand: {
    flexDirection: "row",
    justifyContent: "space-between",
    borderTopWidth: 1,
    borderTopColor: BRAND.primaryDark,
    paddingTop: 6,
    marginTop: 4,
  },
  totalsGrandLabel: { color: BRAND.primaryDark, fontSize: 11 },
  totalsGrandValue: { color: BRAND.primaryDark, fontSize: 11, textAlign: "right" },
  noteBox: {
    marginTop: 18,
    padding: 8,
    backgroundColor: "#fff8e7",
    borderLeftWidth: 2,
    borderLeftColor: "#d4a017",
    fontSize: 8.5,
  },
  paymentBlock: {
    marginTop: 24,
    paddingTop: 12,
    borderTopWidth: 0.5,
    borderTopColor: BRAND.borderColor,
  },
  paymentTitle: { fontSize: 10, color: BRAND.primaryDark, marginBottom: 6 },
  paymentLine: { fontSize: 9, marginBottom: 1 },
  footer: {
    position: "absolute",
    bottom: 24,
    left: 40,
    right: 40,
    flexDirection: "row",
    justifyContent: "space-between",
    fontSize: 7.5,
    color: BRAND.mutedColor,
    borderTopWidth: 0.5,
    borderTopColor: BRAND.borderColor,
    paddingTop: 8,
  },
});

// ---------------------------------------------------------------------------
// Logo — inlined SVG primitives
// ---------------------------------------------------------------------------

/**
 * PieCed honeycomb logo. Re-renders the same 6-hex glyph as the
 * portal's `public/pieced-logo.svg` using React-PDF's SVG support.
 * Width/height are independent of the original viewBox so we can
 * scale it without losing stroke quality.
 */
const Logo = ({ size = 60 }: { size?: number }) => (
  <Svg width={size} height={size * (106 / 70)} viewBox="0 0 70 106">
    {/* H1 solid */}
    <Polygon
      points="38.5,22.69 31.5,10.566 17.5,10.566 10.5,22.69 17.5,34.814 31.5,34.814"
      fill="#10B981"
      stroke="#10B981"
      strokeWidth={1.6}
    />
    {/* H2 outline */}
    <Polygon
      points="59.5,34.814 52.5,22.69 38.5,22.69 31.5,34.814 38.5,46.938 52.5,46.938"
      fill="none"
      stroke="#10B981"
      strokeWidth={1.8}
    />
    {/* H3 outline */}
    <Polygon
      points="38.5,46.938 31.5,34.814 17.5,34.814 10.5,46.938 17.5,59.062 31.5,59.062"
      fill="none"
      stroke="#10B981"
      strokeWidth={1.8}
    />
    {/* H4 solid */}
    <Polygon
      points="59.5,59.062 52.5,46.938 38.5,46.938 31.5,59.062 38.5,71.186 52.5,71.186"
      fill="#10B981"
      stroke="#10B981"
      strokeWidth={1.6}
    />
    {/* H5 partial */}
    <Polyline
      points="31.5,83.31 38.5,71.186 31.5,59.062 17.5,59.062 10.5,71.186"
      fill="none"
      stroke="#10B981"
      strokeWidth={1.8}
    />
    {/* H6 partial */}
    <Polyline
      points="59.5,83.31 52.5,71.186 38.5,71.186 31.5,83.31 38.5,95.434"
      fill="none"
      stroke="#10B981"
      strokeWidth={1.8}
    />
  </Svg>
);

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function fmtChf(n: number, decimals: number = 2): string {
  // Swiss thousands separator + decimal point: 1'234.56
  const fixed = n.toFixed(decimals);
  const [intPart, decPart] = fixed.split(".");
  const withSep = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, "'");
  return decPart ? `${withSep}.${decPart}` : withSep;
}

function fmtDate(iso: string, locale: string): string {
  // Parse YYYY-MM-DD as a calendar date (no timezone shifts).
  // For PDF rendering we want a stable representation regardless
  // of server timezone.
  const [y, m, d] = iso.split("T")[0].split("-").map(Number);
  // Locale-specific date format
  if (locale === "en") {
    return new Date(Date.UTC(y, m - 1, d)).toLocaleDateString("en-US", {
      year: "numeric",
      month: "short",
      day: "numeric",
      timeZone: "UTC",
    });
  }
  // DE/FR/IT default: DD.MM.YYYY
  return `${String(d).padStart(2, "0")}.${String(m).padStart(2, "0")}.${y}`;
}

// ---------------------------------------------------------------------------
// Document
// ---------------------------------------------------------------------------

interface InvoicePdfProps {
  invoice: Invoice;
  lines: InvoiceLine[];
}

const InvoicePdf: React.FC<InvoicePdfProps> = ({ invoice, lines }) => {
  const s = getStrings(invoice.locale);
  const snap = invoice.billingSnapshot;

  // Group lines by tenant for visual separation. Lines without a
  // tenant_name (org-level adjustments) go to the end.
  const linesByTenant = new Map<string | null, InvoiceLine[]>();
  for (const ln of lines) {
    const key = ln.tenantName;
    if (!linesByTenant.has(key)) linesByTenant.set(key, []);
    linesByTenant.get(key)!.push(ln);
  }
  const tenantOrder = [...linesByTenant.keys()].sort((a, b) => {
    if (a === null) return 1;
    if (b === null) return -1;
    return a.localeCompare(b);
  });

  // VAT note: pick the right localized note based on rate + address.
  // Zero rate + EU country = reverse charge; zero rate + other = export.
  let vatNote: string | null = null;
  if (invoice.vatRate === 0) {
    const country = (snap.country || "").toUpperCase();
    const isEu = [
      "AT","BE","BG","HR","CY","CZ","DK","EE","FI","FR","DE","GR","HU",
      "IE","IT","LV","LT","LU","MT","NL","PL","PT","RO","SK","SI","ES","SE",
    ].includes(country);
    vatNote = isEu ? s.reverseCharge : s.exportNote;
  }

  return (
    <Document title={`${s.invoice} ${invoice.invoiceNumber}`}>
      <Page size="A4" style={styles.page}>
        {/* Header: logo left, issuer right */}
        <View style={styles.headerRow}>
          <View style={styles.logoWrap}>
            <Logo size={60} />
          </View>
          <View style={styles.issuerBlock}>
            <Text style={styles.issuerName}>{BRAND.issuer.legalName}</Text>
            <Text>{BRAND.issuer.addressLine1}</Text>
            <Text>{BRAND.issuer.addressLine2}</Text>
            <Text>{BRAND.issuer.postalCity}</Text>
            <Text>{BRAND.issuer.country}</Text>
            <Text>{BRAND.issuer.email}</Text>
            {BRAND.issuer.vatNumber && (
              <Text>MWST-Nr. {BRAND.issuer.vatNumber}</Text>
            )}
          </View>
        </View>

        <Text style={styles.invoiceTitle}>{s.invoice}</Text>

        {/* Meta row: 3 columns */}
        <View style={styles.metaTable}>
          <View style={styles.metaCol}>
            <Text style={styles.metaLabel}>{s.invoiceNumber}</Text>
            <Text style={styles.metaValue}>{invoice.invoiceNumber}</Text>
            <Text style={styles.metaLabel}>{s.issueDate}</Text>
            <Text style={styles.metaValue}>
              {fmtDate(invoice.issuedAt, invoice.locale)}
            </Text>
          </View>
          <View style={styles.metaCol}>
            <Text style={styles.metaLabel}>{s.period}</Text>
            <Text style={styles.metaValue}>
              {fmtDate(invoice.periodStart, invoice.locale)} —{" "}
              {fmtDate(invoice.periodEnd, invoice.locale)}
            </Text>
            <Text style={styles.metaLabel}>{s.dueDate}</Text>
            <Text style={styles.metaValue}>
              {fmtDate(invoice.dueAt, invoice.locale)}
            </Text>
          </View>
        </View>

        {/* Bill-to */}
        <View style={styles.billToBlock}>
          <Text style={styles.billToLabel}>{s.billTo}</Text>
          <Text style={styles.billToName}>{snap.companyName}</Text>
          {/* Phase 6 fix: optional "z.Hd." / "Attn:" line for routing
              the printed invoice internally at the customer. Prints
              between the company name and street address, in the
              invoice's locale (frozen at issue time). */}
          {snap.contactName && (
            <Text>
              {s.attentionPrefix} {snap.contactName}
            </Text>
          )}
          <Text>{snap.streetAddress}</Text>
          <Text>
            {snap.postalCode} {snap.city}
          </Text>
          <Text>{snap.country}</Text>
          {snap.vatNumber && <Text>VAT: {snap.vatNumber}</Text>}
          <Text>{snap.billingEmail}</Text>
        </View>

        {/* Line items table */}
        <View style={styles.table}>
          <View style={styles.tableHeader}>
            <Text style={styles.colDesc}>{s.description}</Text>
            <Text style={styles.colQty}>{s.quantity}</Text>
            <Text style={styles.colUnit}>{s.unitPrice}</Text>
            <Text style={styles.colAmt}>{s.amount} (CHF)</Text>
          </View>
          {tenantOrder.map((tenantKey) => {
            const tenantLines = linesByTenant.get(tenantKey)!;
            return (
              <View key={tenantKey ?? "_org"}>
                {tenantKey && (
                  <View
                    style={{
                      paddingVertical: 4,
                      paddingHorizontal: 6,
                      backgroundColor: "#f0f9f4",
                    }}
                  >
                    <Text style={{ fontSize: 9, color: BRAND.primaryDark }}>
                      {tenantKey}
                    </Text>
                  </View>
                )}
                {tenantLines.map((ln) => (
                  <View key={ln.id} style={styles.tableRow}>
                    <Text style={styles.colDesc}>{ln.description}</Text>
                    <Text style={styles.colQty}>
                      {ln.quantity}
                      {ln.unitLabel ? ` ${ln.unitLabel}` : ""}
                    </Text>
                    <Text style={styles.colUnit}>{fmtChf(ln.unitPriceChf, 5)}</Text>
                    <Text style={styles.colAmt}>{fmtChf(ln.amountChf)}</Text>
                  </View>
                ))}
              </View>
            );
          })}
        </View>

        {/* Totals */}
        <View style={styles.totalsBlock}>
          <View style={styles.totalsRow}>
            <Text style={styles.totalsLabel}>{s.subtotal}</Text>
            <Text style={styles.totalsValue}>{fmtChf(invoice.subtotalChf)}</Text>
          </View>
          <View style={styles.totalsRow}>
            <Text style={styles.totalsLabel}>
              {s.vat} ({invoice.vatRate.toFixed(2)}%)
            </Text>
            <Text style={styles.totalsValue}>{fmtChf(invoice.vatAmountChf)}</Text>
          </View>
          <View style={styles.totalsGrand}>
            <Text style={styles.totalsGrandLabel}>
              {s.total} (CHF)
            </Text>
            <Text style={styles.totalsGrandValue}>{fmtChf(invoice.totalChf)}</Text>
          </View>
        </View>

        {vatNote && (
          <View style={styles.noteBox}>
            <Text>{vatNote}</Text>
          </View>
        )}

        {/* Payment instructions */}
        <View style={styles.paymentBlock}>
          <Text style={styles.paymentTitle}>{s.paymentInstructions}</Text>
          <Text style={styles.paymentLine}>{BRAND.issuer.legalName}</Text>
          <Text style={styles.paymentLine}>{BRAND.issuer.bankName}</Text>
          <Text style={styles.paymentLine}>IBAN: {BRAND.issuer.bankIban}</Text>
          <Text style={styles.paymentLine}>BIC: {BRAND.issuer.bankBic}</Text>
          <Text style={[styles.paymentLine, { marginTop: 6, color: BRAND.mutedColor }]}>
            {s.paymentRefHint}
          </Text>
          <Text style={[styles.paymentLine, { marginTop: 12, color: BRAND.primaryDark }]}>
            {s.thankYou}
          </Text>
        </View>

        {/* Footer with page numbers.
            react-pdf API quirks (verified against build errors):
              - The `render` callback on <View> only exposes
                `{ pageNumber, subPageNumber }` — no totalPages.
                Only <Text> gets `{ pageNumber, totalPages,
                subPageNumber, subPageTotalPages }`.
              - <Text>'s render callback must return a STRING
                (or array of strings), not JSX. */}
        <View style={styles.footer} fixed>
          <Text>
            {BRAND.issuer.legalName} · {BRAND.issuer.web} · {BRAND.issuer.email}
          </Text>
          <Text
            render={({ pageNumber, totalPages }) =>
              `${s.page} ${pageNumber} ${s.of} ${totalPages}`
            }
            fixed
          />
        </View>
      </Page>
    </Document>
  );
};

// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------

/**
 * Render an invoice to a PDF buffer. Caller stores the buffer in
 * `invoices.pdf_data` (bytea). Side-effect-free; can be called
 * outside a DB transaction.
 *
 * Typical runtime is 50–200ms on a typical invoice with a dozen
 * lines.
 */
export async function renderInvoicePdf(
  invoice: Invoice,
  lines: InvoiceLine[]
): Promise<Buffer> {
  return renderToBuffer(<InvoicePdf invoice={invoice} lines={lines} />);
}
