import { describe, expect, it } from "vitest";
import {
  formatCents,
  formatDate,
  formatDateLong,
  formatDateMedium,
  formatDateShort,
  formatMonthYear,
  formatMoney,
  toDateInputValue,
} from "./format.js";

// ---------------------------------------------------------------------------
// toDateInputValue
// ---------------------------------------------------------------------------
describe("toDateInputValue", () => {
  it("returns empty string for null", () => {
    expect(toDateInputValue(null)).toBe("");
  });

  it("returns empty string for undefined", () => {
    expect(toDateInputValue(undefined)).toBe("");
  });

  it("formats a Date object to yyyy-mm-dd", () => {
    const d = new Date(2026, 2, 4); // local: 4 March 2026
    expect(toDateInputValue(d)).toBe("2026-03-04");
  });

  it("pads single-digit month and day with leading zeros", () => {
    const d = new Date(2026, 0, 9); // local: 9 January 2026
    expect(toDateInputValue(d)).toBe("2026-01-09");
  });

  it("accepts an ISO date string", () => {
    // Use a date string that is unambiguous when parsed by Date constructor
    const result = toDateInputValue("2026-11-30T00:00:00");
    expect(result).toBe("2026-11-30");
  });

  it("handles end-of-year boundary", () => {
    const d = new Date(2025, 11, 31); // 31 December 2025
    expect(toDateInputValue(d)).toBe("2025-12-31");
  });
});

// ---------------------------------------------------------------------------
// formatDate
// ---------------------------------------------------------------------------
describe("formatDate", () => {
  it("returns empty string for null", () => {
    expect(formatDate(null)).toBe("");
  });

  it("returns empty string for undefined", () => {
    expect(formatDate(undefined)).toBe("");
  });

  it("formats a Date using en-GB locale (dd/mm/yyyy)", () => {
    // Use a date created from a local timestamp so locale formatting is stable
    const d = new Date(2026, 2, 4); // 4 March 2026
    expect(formatDate(d)).toBe("04/03/2026");
  });

  it("accepts a string input", () => {
    const result = formatDate("2026-07-15T00:00:00");
    expect(result).toBe("15/07/2026");
  });
});

// ---------------------------------------------------------------------------
// formatDateShort
// ---------------------------------------------------------------------------
describe("formatDateShort", () => {
  it("returns DD MMM format", () => {
    const d = new Date(2026, 2, 4); // 4 March 2026
    expect(formatDateShort(d)).toBe("04 Mar");
  });

  it("pads day with leading zero", () => {
    const d = new Date(2026, 0, 5); // 5 January 2026
    expect(formatDateShort(d)).toBe("05 Jan");
  });

  it("accepts a date string", () => {
    const result = formatDateShort("2026-12-01T00:00:00");
    expect(result).toBe("01 Dec");
  });
});

// ---------------------------------------------------------------------------
// formatMonthYear
// ---------------------------------------------------------------------------
describe("formatMonthYear", () => {
  it("returns MMM YY format", () => {
    const d = new Date(2026, 2, 1); // March 2026
    expect(formatMonthYear(d)).toBe("Mar 26");
  });

  it("handles year boundary (December 2025)", () => {
    const d = new Date(2025, 11, 1);
    expect(formatMonthYear(d)).toBe("Dec 25");
  });

  it("accepts a date string", () => {
    const result = formatMonthYear("2026-07-01T00:00:00");
    expect(result).toBe("Jul 26");
  });
});

// ---------------------------------------------------------------------------
// formatDateLong
// ---------------------------------------------------------------------------
describe("formatDateLong", () => {
  it("returns long form including full month name and year", () => {
    const d = new Date(2026, 2, 4); // 4 March 2026
    expect(formatDateLong(d)).toBe("4 March 2026");
  });

  it("accepts a date string", () => {
    const result = formatDateLong("2026-01-01T00:00:00");
    expect(result).toBe("1 January 2026");
  });

  it("handles single-digit day without zero-padding", () => {
    const d = new Date(2026, 5, 7); // 7 June 2026
    expect(formatDateLong(d)).toBe("7 June 2026");
  });
});

// ---------------------------------------------------------------------------
// formatDateMedium
// ---------------------------------------------------------------------------
describe("formatDateMedium", () => {
  it("returns empty string for null", () => {
    expect(formatDateMedium(null)).toBe("");
  });

  it("returns empty string for undefined", () => {
    expect(formatDateMedium(undefined)).toBe("");
  });

  it("returns compact date with short month and year", () => {
    const d = new Date(2026, 2, 16); // 16 March 2026
    expect(formatDateMedium(d)).toBe("16 Mar 2026");
  });

  it("accepts a date string", () => {
    const result = formatDateMedium("2026-08-05T00:00:00");
    expect(result).toBe("5 Aug 2026");
  });
});

// ---------------------------------------------------------------------------
// formatMoney
// ---------------------------------------------------------------------------
describe("formatMoney", () => {
  it("formats zero cents as 0 EUR", () => {
    // The de-DE locale uses non-breaking space before the currency symbol
    const result = formatMoney(0);
    expect(result).toContain("0");
    expect(result).toContain("€");
  });

  it("formats null as 0 EUR", () => {
    const result = formatMoney(null);
    expect(result).toContain("0");
    expect(result).toContain("€");
  });

  it("formats undefined as 0 EUR", () => {
    const result = formatMoney(undefined);
    expect(result).toContain("0");
    expect(result).toContain("€");
  });

  it("converts cents to euros (divides by 100)", () => {
    // 123400 cents = 1234 EUR, de-DE: "1.234 €"
    const result = formatMoney(123400);
    expect(result).toContain("1.234");
    expect(result).toContain("€");
  });

  it("rounds to whole euros by default (fractionDigits = 0)", () => {
    const result = formatMoney(9950); // 99.50 EUR → rounds to 100
    expect(result).toContain("100");
  });

  it("respects fractionDigits = 2 for precise display", () => {
    const result = formatMoney(9950, "EUR", 2); // 99.50 EUR
    expect(result).toContain("99,50");
  });

  it("supports other currencies", () => {
    const result = formatMoney(10000, "USD", 0); // 100 USD
    expect(result).toContain("100");
    expect(result).toContain("$");
  });

  it("formats negative values correctly", () => {
    const result = formatMoney(-50000); // -500 EUR
    expect(result).toContain("500");
    // de-DE locale may use a regular hyphen-minus or a minus sign
    const hasNegativeSign = result.includes("−") || result.includes("-");
    expect(hasNegativeSign).toBe(true);
  });
});

// ---------------------------------------------------------------------------
// formatCents
// ---------------------------------------------------------------------------
describe("formatCents", () => {
  it("returns '-' for null", () => {
    expect(formatCents(null)).toBe("-");
  });

  it("returns '-' for undefined", () => {
    expect(formatCents(undefined)).toBe("-");
  });

  it("formats zero as '0,00'", () => {
    expect(formatCents(0)).toBe("0,00");
  });

  it("formats 100 cents as '1,00'", () => {
    expect(formatCents(100)).toBe("1,00");
  });

  it("formats 123456 cents as '1.234,56' (de-DE locale)", () => {
    expect(formatCents(123456)).toBe("1.234,56");
  });

  it("formats negative cents correctly", () => {
    expect(formatCents(-100)).toBe("-1,00");
  });

  it("always shows exactly 2 decimal places", () => {
    expect(formatCents(50)).toBe("0,50");
    expect(formatCents(5)).toBe("0,05");
  });
});

// ---------------------------------------------------------------------------
// edge cases
// ---------------------------------------------------------------------------
describe("edge cases", () => {
  it("formatCents with NaN input — passes through toLocaleString which returns 'NaN'", () => {
    // NaN == null is false, so it reaches the toLocaleString branch
    // NaN / 100 is NaN; de-DE toLocaleString of NaN returns "NaN"
    const result = formatCents(NaN);
    expect(result).toBe("NaN");
  });

  it("formatCents with Number.MAX_SAFE_INTEGER — no precision loss in string output", () => {
    // Verify it returns a non-empty, numeric-looking string and not '-'
    const result = formatCents(Number.MAX_SAFE_INTEGER);
    expect(result).not.toBe("-");
    expect(result.length).toBeGreaterThan(0);
    // The integer part should contain the expected leading digits (90071992547409)
    expect(result).toContain("90.071.992.547.409");
  });

  it("toDateInputValue with an invalid date string — returns 'NaN-NaN-NaN'", () => {
    // new Date("not-a-date") produces an Invalid Date; getFullYear() etc. return NaN
    expect(toDateInputValue("not-a-date")).toBe("NaN-NaN-NaN");
  });

  it("formatMoney with 0 cents — returns the zero euro representation", () => {
    const result = formatMoney(0);
    // de-DE locale: "0 €" (with non-breaking space before €)
    expect(result).toContain("0");
    expect(result).toContain("€");
  });
});
