/**
 * Sprint E: tax jurisdiction, date parsing, payment method,
 * bounding-box honesty, leftover product fixes.
 */

import { describe, test, expect } from "./runner";
import { PENDING_VALIDATION, ReceiptData } from "../../src/lib/schema/receipt";
import { reconcileAndEnhanceReceiptData } from "../../src/lib/ai/extractor";
import {
  inferTaxCountry,
  resolveTaxRatePercent,
  snapVatRate,
} from "../../src/lib/tax/rates";
import { parseReceiptDate } from "../../src/lib/parse/receiptDate";
import { normalizePaymentMethod } from "../../src/lib/parse/paymentMethod";
import { hasExtractedBoundingBoxes } from "../../src/lib/utils/boundingBoxes";
import {
  parseTaxRatePercent,
  taxTotalsByRate,
} from "../../src/components/dashboard/receiptFormat";
import { sanitizeExtractionOutput } from "../../src/lib/ai/promptInjection";

function receipt(overrides: Partial<ReceiptData> = {}): ReceiptData {
  return {
    merchant: { name: "Shop", address: null, taxId: null, confidence: 0.9 },
    date: { isoDate: "2026-08-12", time: null, confidence: 0.9 },
    documentType: "KASSENBON",
    receiptNumber: null,
    currency: "EUR",
    totalAmount: { value: 119, confidence: 0.9 },
    netAmount: 100,
    taxBreakdown: [],
    lineItems: [],
    suggestedCategory: "Sonstiges",
    validation: { ...PENDING_VALIDATION },
    ...overrides,
  };
}

describe("Sprint E — Steuerland", () => {
  test("ATU / Wien → AT, CHE / Zürich → CH, DE → DE, USD → OTHER", () => {
    expect(inferTaxCountry({ taxId: "ATU12345678", currency: "EUR" })).toBe("AT");
    expect(inferTaxCountry({ address: "Kärntner Straße 1, Wien", currency: "EUR" })).toBe("AT");
    expect(inferTaxCountry({ taxId: "CHE-123.456.789 MWST", currency: "CHF" })).toBe("CH");
    expect(inferTaxCountry({ currency: "CHF" })).toBe("CH");
    expect(inferTaxCountry({ taxId: "DE123456789", currency: "EUR" })).toBe("DE");
    expect(inferTaxCountry({ currency: "EUR" })).toBe("DACH");
    expect(inferTaxCountry({ currency: "USD" })).toBe("OTHER");
  });

  test("AT 20% wird nicht auf DE 19% gesnappt", () => {
    expect(snapVatRate(20, "AT")).toBe(20);
    expect(snapVatRate(19.2, "AT")).toBe(20);
    expect(snapVatRate(19, "DE")).toBe(19);
    expect(snapVatRate(8.1, "CH")).toBe(8.1);
    expect(snapVatRate(8.05, "CH")).toBe(8.1);
    expect(snapVatRate(8.75, "OTHER")).toBe(8.75);
    expect(snapVatRate(20, "DACH")).toBe(20);
    expect(snapVatRate(19, "DACH")).toBe(19);
  });

  test("fehlender Steuersatz wird nicht mit 19 gefüllt", () => {
    expect(resolveTaxRatePercent({ taxAmount: 0, netAmount: 10 }, "DE")).toBe(0);
    expect(
      resolveTaxRatePercent({ taxAmount: 20, netAmount: 100 }, "AT")
    ).toBe(20);
  });
});

describe("Sprint E — Reconcile Steuersätze", () => {
  test("AT-Beleg mit Netto/Brutto bekommt 20%, nicht 19%", () => {
    const out = reconcileAndEnhanceReceiptData(
      receipt({
        merchant: { name: "Billa", address: "Wien", taxId: "ATU12345678", confidence: 0.9 },
        totalAmount: { value: 120, confidence: 0.9 },
        netAmount: 100,
        taxBreakdown: [],
      })
    );
    expect(out.taxBreakdown).toHaveLength(1);
    expect(out.taxBreakdown[0].ratePercent).toBe(20);
    expect(out.taxBreakdown[0].taxAmount).toBe(20);
  });

  test("CH-Beleg mit CHF wird auf 8.1% gesnappt", () => {
    const out = reconcileAndEnhanceReceiptData(
      receipt({
        merchant: { name: "Migros", address: "Zürich", taxId: "CHE-123.456.789", confidence: 0.9 },
        currency: "CHF",
        totalAmount: { value: 108.1, confidence: 0.9 },
        netAmount: 100,
        taxBreakdown: [],
      })
    );
    expect(out.taxBreakdown[0].ratePercent).toBe(8.1);
  });

  test("DE-Beleg bleibt bei 19%", () => {
    const out = reconcileAndEnhanceReceiptData(
      receipt({
        merchant: { name: "REWE", address: "Berlin", taxId: "DE123456789", confidence: 0.9 },
        totalAmount: { value: 119, confidence: 0.9 },
        netAmount: 100,
        taxBreakdown: [],
      })
    );
    expect(out.taxBreakdown[0].ratePercent).toBe(19);
  });

  test("EUR ohne Land-Hinweis: 20% bleibt 20, nicht 19", () => {
    const out = reconcileAndEnhanceReceiptData(
      receipt({
        merchant: { name: "Spar", address: null, taxId: null, confidence: 0.9 },
        currency: "EUR",
        totalAmount: { value: 120, confidence: 0.9 },
        netAmount: 100,
        taxBreakdown: [],
      })
    );
    expect(out.taxBreakdown[0].ratePercent).toBe(20);
  });

  test("US-Beleg ohne DACH-Satz bleibt bei berechnetem Satz", () => {
    const out = reconcileAndEnhanceReceiptData(
      receipt({
        merchant: { name: "Walgreens", address: "United States", taxId: null, confidence: 0.9 },
        currency: "USD",
        totalAmount: { value: 108.75, confidence: 0.9 },
        netAmount: 100,
        taxBreakdown: [],
      })
    );
    expect(out.taxBreakdown[0].ratePercent).toBe(8.75);
  });

  test("leere ratePercent-Zeile wird 0, nicht 19", () => {
    const out = reconcileAndEnhanceReceiptData(
      receipt({
        taxBreakdown: [{ ratePercent: undefined as unknown as number, taxAmount: 0, netAmount: 50 }],
      })
    );
    expect(out.taxBreakdown[0].ratePercent).toBe(0);
  });
});

describe("Sprint E — Datumsparser", () => {
  test("ISO bleibt ISO", () => {
    expect(parseReceiptDate("2026-08-16")).toBe("2026-08-16");
  });

  test("DACH-Punkt-Datum ist Tag.Monat.Jahr", () => {
    expect(parseReceiptDate("16.08.2026")).toBe("2026-08-16");
    expect(parseReceiptDate("16.08.26")).toBe("2026-08-16");
  });

  test("Jahr-zuerst mit Punkt/Slash", () => {
    expect(parseReceiptDate("2026/08/16")).toBe("2026-08-16");
  });

  test("eindeutige Slash-Daten: Tag > 12 vs Monat > 12", () => {
    expect(parseReceiptDate("16/08/2026")).toBe("2026-08-16");
    expect(parseReceiptDate("08/16/2026")).toBe("2026-08-16");
  });

  test("mehrdeutiges 01/02/2026: EUR Tag zuerst, USD Monat zuerst", () => {
    expect(parseReceiptDate("01/02/2026", { currency: "EUR" })).toBe("2026-02-01");
    expect(parseReceiptDate("01/02/2026", { currency: "USD" })).toBe("2026-01-02");
  });

  test("Reconcile wandelt 16.08.2026 nach ISO", () => {
    const out = reconcileAndEnhanceReceiptData(
      receipt({ date: { isoDate: "16.08.2026", time: null, confidence: 0.9 } })
    );
    expect(out.date.isoDate).toBe("2026-08-16");
  });

  test("Sanitize parst DACH-Datum statt es zu verwerfen", () => {
    const out = sanitizeExtractionOutput(
      receipt({ date: { isoDate: "03.01.2026", time: "10:00", confidence: 0.9 } })
    );
    expect(out.date.isoDate).toBe("2026-01-03");
  });
});

describe("Sprint E — Zahlart", () => {
  test("Enum und gängige Belegtexte werden gemappt", () => {
    expect(normalizePaymentMethod("EC_KARTE")).toBe("EC_KARTE");
    expect(normalizePaymentMethod("girocard")).toBe("EC_KARTE");
    expect(normalizePaymentMethod("EC-Karte")).toBe("EC_KARTE");
    expect(normalizePaymentMethod("Visa")).toBe("KREDITKARTE");
    expect(normalizePaymentMethod("Bar")).toBe("BAR");
    expect(normalizePaymentMethod("Apple Pay")).toBe("APPLE_PAY");
    expect(normalizePaymentMethod("PayPal")).toBe("PAYPAL");
    expect(normalizePaymentMethod("Überweisung")).toBe("UEBERWEISUNG");
    expect(normalizePaymentMethod("")).toBeNull();
    expect(normalizePaymentMethod(null)).toBeNull();
  });

  test("Reconcile übernimmt gemappte Zahlart", () => {
    const out = reconcileAndEnhanceReceiptData(
      receipt({ paymentMethod: "girocard" as never })
    );
    expect(out.paymentMethod).toBe("EC_KARTE");
  });
});

describe("Sprint E — Manuelle Steuersätze", () => {
  test("parseTaxRatePercent akzeptiert 7, 19, 20 und Kommawerte", () => {
    expect(parseTaxRatePercent("7")).toBe(7);
    expect(parseTaxRatePercent("19%")).toBe(19);
    expect(parseTaxRatePercent("20")).toBe(20);
    expect(parseTaxRatePercent("8,1")).toBe(8.1);
    expect(parseTaxRatePercent("")).toBeNull();
    expect(parseTaxRatePercent("150")).toBe(100);
  });

  test("taxTotalsByRate aggregiert beliebige Sätze", () => {
    const rows = taxTotalsByRate([
      receipt({
        taxBreakdown: [
          { ratePercent: 20, taxAmount: 20, netAmount: 100 },
          { ratePercent: 10, taxAmount: 5, netAmount: 50 },
        ],
      }),
    ]);
    expect(rows.map((r) => r.rate)).toEqual([10, 20]);
    expect(rows[1].amount).toBe(20);
  });
});

describe("Sprint E — Bounding Boxes", () => {
  test("ohne OCR-Koordinaten keine Overlay-Boxen", () => {
    expect(hasExtractedBoundingBoxes(undefined)).toBe(false);
    expect(hasExtractedBoundingBoxes({})).toBe(false);
    expect(
      hasExtractedBoundingBoxes({
        merchant: { x: 10, y: 10, width: 40, height: 8 },
      })
    ).toBe(true);
  });
});
