/**
 * PDF-Export: Struktur, Lokalisierung und Datenintegrität.
 *
 * Die Textebene wird mit pdfjs-dist (Legacy-Build, wie im ImageProcessor)
 * extrahiert – der schnellste Weg, zu prüfen, dass die Zahlen wirklich im
 * PDF stehen und die Lokalisierung sauber ist.
 */

import { describe, test, expect } from "./runner";
import { generateReceiptPdf } from "../../src/lib/export/pdfGenerator";
import { ProcessedReceipt } from "../../src/lib/schema/receipt";

function receipt(overrides: Partial<ProcessedReceipt> = {}): ProcessedReceipt {
  return {
    merchant: { name: "REWE", address: null, taxId: "DE123456789", confidence: 0.97 },
    date: { isoDate: "2026-08-12", time: null, confidence: 0.95 },
    documentType: "KASSENBON",
    receiptNumber: "1",
    currency: "EUR",
    totalAmount: { value: 31.8, confidence: 0.98 },
    netAmount: 26.72,
    tipAmount: null,
    taxBreakdown: [{ ratePercent: 19, taxAmount: 5.08, netAmount: 26.72 }],
    lineItems: [
      { description: "Vollmilch 3,5%", quantity: 2, price: 2.38, unitPrice: 1.19, taxRate: 7 },
      { description: "Bio-Baguette", quantity: 1, price: 1.79, unitPrice: 1.79, taxRate: 19 },
    ],
    suggestedCategory: "Material & Einkauf",
    validation: {
      isMathValid: true,
      isDuplicateSuspected: false,
      needsUserReview: false,
      reviewField: "none",
      reviewReason: null,
    },
    id: "r1",
    imageHash: "h1",
    originalFileName: "r1.jpg",
    fileSizeBytes: 1024,
    createdAt: "2026-08-12T10:00:00.000Z",
    updatedAt: "2026-08-12T10:00:00.000Z",
    status: "ready",
    ...overrides,
  } as ProcessedReceipt;
}

/** Extrahiert alle Texte eines generierten PDFs (pdfjs-Legacy-Build, Node). */
async function pdfText(bytes: Uint8Array): Promise<{ text: string[]; pages: number }> {
  const mod: any = await import("pdfjs-dist/legacy/build/pdf.mjs");
  const pdfjs = mod.default ?? mod;
  const task = pdfjs.getDocument({
    // Kopie: pdfjs übernimmt den Puffer.
    data: new Uint8Array(bytes),
    isEvalSupported: false,
    useSystemFonts: false,
    disableFontFace: true,
  });
  const doc: any = await task.promise;
  const text: string[] = [];
  try {
    for (let i = 1; i <= doc.numPages; i++) {
      const page: any = await doc.getPage(i);
      const content: any = await page.getTextContent();
      for (const item of content.items ?? []) text.push(String(item.str ?? ""));
      page.cleanup();
    }
  } finally {
    await task.destroy().catch(() => undefined);
  }
  return { text, pages: doc.numPages };
}

const joined = (lines: string[]) => lines.join("\n");

describe("PDF-Export: Struktur", () => {
  test("P-1: erzeugt eine gültige PDF-Datei", async () => {
    const bytes = await generateReceiptPdf([receipt()]);
    expect(bytes[0]).toBe(0x25); // '%'
    expect(String.fromCharCode(bytes[1])).toBe("P");
    expect(String.fromCharCode(bytes[2])).toBe("D");
    expect(String.fromCharCode(bytes[3])).toBe("F");
    expect(bytes.length).toBeGreaterThan(1000);
  });

  test("P-2: leerer Datensatz erzeugt eine gültige, lesbare PDF", async () => {
    const bytes = await generateReceiptPdf([]);
    const { text } = await pdfText(bytes);
    expect(joined(text)).toContain("BELEGE EXPORT");
    expect(bytes[0]).toBe(0x25);
  });

  test("P-3: die Kartenzahl wächst mit der Belegzahl (Seitenumbruch)", async () => {
    const one = await generateReceiptPdf([receipt()]);
    const many = await generateReceiptPdf(
      Array.from({ length: 14 }, (_, i) =>
        receipt({ id: `r${i}`, totalAmount: { value: 31.8 + i, confidence: 0.98 } })
      )
    );
    const { pages: pOne } = await pdfText(one);
    const { pages: pMany } = await pdfText(many);
    expect(pMany).toBeGreaterThanOrEqual(pOne);
  });
});

describe("PDF-Export: Datenintegrität", () => {
  test("P-4: Händler, Beträge und Positionen stehen im Dokument", async () => {
    const { text } = await pdfText(await generateReceiptPdf([receipt()]));
    const all = joined(text);
    expect(all).toContain("REWE");
    expect(all).toContain("26,72 €"); // Netto
    expect(all).toContain("5,08 €"); // MwSt 19 %
    expect(all).toContain("31,80 €"); // Brutto
    expect(all).toContain("Vollmilch 3,5%");
    expect(all).toContain("1,19 €"); // Einzelpreis
    expect(all).toContain("DE123456789"); // Steuernummer
  });

  test("P-5: Trinkgeld erscheint als eigene Zeile inkl. Gesamt gezahlt", async () => {
    const r = receipt({ tipAmount: 5, totalAmount: { value: 31.8, confidence: 0.98 } });
    const { text } = await pdfText(await generateReceiptPdf([r]));
    const all = joined(text);
    expect(all).toContain("Trinkgeld");
    expect(all).toContain("5,00 €");
    expect(all).toContain("Gesamt gezahlt");
    expect(all).toContain("36,80 €");
  });

  test("P-6: ungeprüfter Beleg erhält Status Prüfen (n)", async () => {
    const r = receipt({
      validation: {
        isMathValid: false,
        isDuplicateSuspected: false,
        needsUserReview: true,
        reviewField: "totalAmount",
        reviewReason: "x",
        issues: [{ field: "totalAmount", severity: "error", message: "x" }],
      },
    });
    const { text } = await pdfText(await generateReceiptPdf([r]));
    expect(joined(text)).toContain("Prüfen (1)");
  });

  test("P-7: Bewirtungsangaben werden übernommen", async () => {
    const r = receipt({
      documentType: "BEWIRTUNGSBELEG",
      suggestedCategory: "Bewirtung",
      hospitality: { occasion: "Kundenbesuch", participants: "Max Mustermann" },
    });
    const { text } = await pdfText(await generateReceiptPdf([r]));
    const all = joined(text);
    expect(all).toContain("Kundenbesuch");
    expect(all).toContain("Max Mustermann");
  });

  test("P-8: gemischte Währungen werden nicht zu einer Summe vermischt", async () => {
    const eur = receipt({ id: "a", currency: "EUR", totalAmount: { value: 10, confidence: 0.98 } });
    const usd = receipt({ id: "b", currency: "USD", totalAmount: { value: 5, confidence: 0.98 } });
    const { text } = await pdfText(await generateReceiptPdf([eur, usd]));
    const all = joined(text);
    expect(all).toContain("mehrere Währungen");
    expect(all).toContain("EUR: 10,00 €");
    expect(all).toContain("USD: 5,00 USD");
  });

  test("P-9: unicode-gefährliche Zeichen werden entschärft statt zu brechen", async () => {
    const r = receipt({
      merchant: { name: "CAFÉ ✓ BERLIN ☕", address: null, taxId: null, confidence: 0.9 },
    });
    const bytes = await generateReceiptPdf([r]);
    const { text } = await pdfText(bytes);
    expect(joined(text)).toContain("CAFÉ");
    expect(joined(text)).toContain("BERLIN");
  });
});

describe("PDF-Export: Lokalisierung", () => {
  test("P-10: Standard bleibt Deutsch", async () => {
    const { text } = await pdfText(await generateReceiptPdf([receipt()]));
    expect(joined(text)).toContain("BELEGE EXPORT");
    expect(joined(text)).toContain("Netto");
    expect(joined(text)).toContain("Brutto Gesamt");
  });

  test("P-11: locale 'en' übersetzt Titel und Beschriftungen", async () => {
    const { text } = await pdfText(await generateReceiptPdf([receipt()], { locale: "en" }));
    const all = joined(text);
    expect(all).toContain("RECEIPT EXPORT");
    expect(all).toContain("Net");
    expect(all).toContain("Gross total");
    expect(all).toContain("VAT 19%");
  });

  test("P-12: im englischen Export bleibt kein deutsches Label stehen", async () => {
    const { text } = await pdfText(await generateReceiptPdf([receipt()], { locale: "en" }));
    const german = ["BELEGE EXPORT", "Netto", "Brutto Gesamt", "MwSt gesamt", "Trinkgeld", "Prüfen"];
    const all = joined(text);
    for (const g of german) expect(all).not.toContain(g);
  });

  test("P-13: unbekannte Sprache fällt auf Deutsch zurück", async () => {
    const bytes = await generateReceiptPdf(
      [receipt()],
      { locale: "fr" as unknown as "de" }
    );
    const { text } = await pdfText(bytes);
    expect(joined(text)).toContain("BELEGE EXPORT");
  });

  test("P-14: Zeitraum erscheint im Untertitel", async () => {
    const { text } = await pdfText(
      await generateReceiptPdf([receipt()], { dateFrom: "2026-01-01", dateTo: "2026-12-31" })
    );
    expect(joined(text)).toContain("Zeitraum");
    expect(joined(text)).toContain("01.01.2026");
  });
});