import { describe, it, expect } from "vitest";
import { __testables, type InvoiceItem, type InvoiceOrder } from "../invoicePdf";

const { isPaidImmediately, groupByTaxRate } = __testables;

const item = (overrides: Partial<InvoiceItem> = {}): InvoiceItem => ({
  productName: "ToDo-Karten",
  quantity: 2,
  unitPrice: 12.9,
  taxRatePercent: 19,
  bundleContents: null,
  ...overrides,
});

const order = (overrides: Partial<InvoiceOrder> = {}): InvoiceOrder => ({
  orderNumber: "#EP-0001",
  invoiceNumber: "RE-0001",
  invoiceIssuedAt: new Date().toISOString(),
  customerFirstName: "Max",
  customerLastName: "Mustermann",
  deliveryMethod: "address",
  street: "Musterweg 1",
  zip: "10115",
  city: "Berlin",
  country: "Deutschland",
  paymentMethodTitle: "Kreditkarte",
  items: [item()],
  subtotal: 25.8,
  shippingCost: 2.9,
  discountAmount: 0,
  discountCode: null,
  total: 28.7,
  ...overrides,
});

describe("isPaidImmediately", () => {
  it("is true for Kreditkarte", () => {
    expect(isPaidImmediately("Kreditkarte")).toBe(true);
  });

  it("is true for PayPal", () => {
    expect(isPaidImmediately("PayPal")).toBe(true);
  });

  it("is false only for Überweisung", () => {
    expect(isPaidImmediately("Überweisung")).toBe(false);
  });

  it("defaults to true for any future/unknown payment method (only Überweisung is the named exception)", () => {
    expect(isPaidImmediately("Sofortüberweisung")).toBe(true);
    expect(isPaidImmediately("Klarna")).toBe(true);
  });
});

describe("groupByTaxRate (original invoice)", () => {
  it("reconciles net+tax to gross, and gross to the order total for a single rate", () => {
    const o = order({ items: [item({ quantity: 2, unitPrice: 12.9 })], subtotal: 25.8, shippingCost: 2.9, discountAmount: 0, total: 28.7 });
    const groups = groupByTaxRate(o, 19);
    expect(groups).toHaveLength(1);
    const [g] = groups;
    expect(g.net + g.tax).toBeCloseTo(g.gross, 6);
    expect(g.gross).toBeCloseTo(28.7, 2);
  });

  it("distributes a discount proportionally, still reconciling to the discounted total", () => {
    const o = order({
      items: [item({ quantity: 1, unitPrice: 50 })],
      subtotal: 50,
      shippingCost: 0,
      discountAmount: 10,
      total: 40,
    });
    const groups = groupByTaxRate(o, 19);
    const total = groups.reduce((sum, g) => sum + g.gross, 0);
    expect(total).toBeCloseTo(40, 2);
  });

  it("splits multiple tax rates into separate groups that each reconcile", () => {
    const o = order({
      items: [item({ quantity: 1, unitPrice: 20, taxRatePercent: 19 }), item({ quantity: 1, unitPrice: 10, taxRatePercent: 7 })],
      subtotal: 30,
      shippingCost: 0,
      discountAmount: 0,
      total: 30,
    });
    const groups = groupByTaxRate(o, 19);
    expect(groups).toHaveLength(2);
    for (const g of groups) expect(g.net + g.tax).toBeCloseTo(g.gross, 6);
    expect(groups.reduce((sum, g) => sum + g.gross, 0)).toBeCloseTo(30, 2);
  });

  it("falls back to the tenant default rate when an item has no taxRatePercent", () => {
    const o = order({ items: [item({ taxRatePercent: undefined as unknown as number })] });
    const groups = groupByTaxRate(o, 7);
    expect(groups[0].rate).toBe(7);
  });
});
