import { describe, expect, test } from "bun:test";
import {
  bucketByDate,
  filterByDay,
  isToday,
  isoDate,
  shift,
  startOfDay,
  startOfWeek,
  type CalendarItem,
} from "./mount-calendar";

// Regression tests for t-paliad-224: the calendar bucket / week / shift
// helpers underpin both /events Kalender and the Custom Views shape=
// calendar. DOM-rendering is covered by manual smoke (frontend tests in
// this repo run in plain Node, no jsdom — see verfahrensablauf-core.test
// ts comment), so the pure date-math goes here.

const item = (overrides: Partial<CalendarItem> = {}): CalendarItem => ({
  kind: "deadline",
  id: "00000000-0000-0000-0000-000000000000",
  title: "Klageerwiderung",
  event_date: "2026-05-08T00:00:00Z",
  ...overrides,
});

describe("isoDate / startOfDay / startOfWeek", () => {
  test("isoDate pads month + day", () => {
    expect(isoDate(new Date(2026, 0, 3))).toBe("2026-01-03");
    expect(isoDate(new Date(2026, 11, 31))).toBe("2026-12-31");
  });

  test("startOfDay strips time", () => {
    const d = new Date(2026, 4, 8, 13, 47, 22);
    const out = startOfDay(d);
    expect(out.getHours()).toBe(0);
    expect(out.getMinutes()).toBe(0);
    expect(out.getSeconds()).toBe(0);
    expect(isoDate(out)).toBe("2026-05-08");
  });

  test("startOfWeek snaps to Monday (Mon=0)", () => {
    // 2026-05-08 was a Friday.
    const fri = new Date(2026, 4, 8);
    expect(isoDate(startOfWeek(fri))).toBe("2026-05-04");
    // Sunday wraps backward to the same Monday, not forward to the next.
    const sun = new Date(2026, 4, 10);
    expect(isoDate(startOfWeek(sun))).toBe("2026-05-04");
    // Monday is its own startOfWeek.
    const mon = new Date(2026, 4, 4);
    expect(isoDate(startOfWeek(mon))).toBe("2026-05-04");
  });
});

describe("shift", () => {
  test("month shift lands on day=1 of the target month", () => {
    const out = shift(new Date(2026, 4, 15), "month", 1);
    expect(out.getFullYear()).toBe(2026);
    expect(out.getMonth()).toBe(5);
    expect(out.getDate()).toBe(1);
  });

  test("month shift wraps year boundary", () => {
    const out = shift(new Date(2026, 11, 15), "month", 1);
    expect(out.getFullYear()).toBe(2027);
    expect(out.getMonth()).toBe(0);
    expect(out.getDate()).toBe(1);
  });

  test("week shift moves seven days", () => {
    const out = shift(new Date(2026, 4, 8), "week", 1);
    expect(isoDate(out)).toBe("2026-05-15");
  });

  test("day shift moves one day", () => {
    const out = shift(new Date(2026, 4, 8), "day", -1);
    expect(isoDate(out)).toBe("2026-05-07");
  });
});

describe("bucketByDate", () => {
  test("groups items by ISO date and skips items outside the filter", () => {
    const rows = [
      item({ id: "a", event_date: "2026-05-08T00:00:00Z" }),
      item({ id: "b", event_date: "2026-05-08T15:30:00Z" }),
      item({ id: "c", event_date: "2026-05-09T00:00:00Z" }),
      // outside the May 2026 filter:
      item({ id: "x", event_date: "2026-06-01T00:00:00Z" }),
      // malformed:
      item({ id: "bad", event_date: "not-a-date" }),
    ];
    const out = bucketByDate(rows, (d) => d.getMonth() === 4 && d.getFullYear() === 2026);
    expect(out.size).toBe(2);
    expect(out.get("2026-05-08")?.map((r) => r.id)).toEqual(["a", "b"]);
    expect(out.get("2026-05-09")?.map((r) => r.id)).toEqual(["c"]);
    expect(out.has("2026-06-01")).toBe(false);
  });
});

describe("filterByDay", () => {
  test("returns only items whose calendar day equals the target", () => {
    const rows = [
      item({ id: "a", event_date: "2026-05-08T00:00:00Z" }),
      item({ id: "b", event_date: "2026-05-08T23:59:00Z" }),
      item({ id: "c", event_date: "2026-05-09T00:00:00Z" }),
    ];
    expect(filterByDay(rows, new Date(2026, 4, 8)).map((r) => r.id)).toEqual(["a", "b"]);
    expect(filterByDay(rows, new Date(2026, 4, 9)).map((r) => r.id)).toEqual(["c"]);
    expect(filterByDay(rows, new Date(2026, 4, 10))).toEqual([]);
  });

  test("ignores malformed dates", () => {
    const rows = [
      item({ id: "ok", event_date: "2026-05-08T00:00:00Z" }),
      item({ id: "bad", event_date: "not-a-date" }),
    ];
    expect(filterByDay(rows, new Date(2026, 4, 8)).map((r) => r.id)).toEqual(["ok"]);
  });
});

describe("isToday", () => {
  test("matches today's calendar day", () => {
    expect(isToday(new Date())).toBe(true);
  });

  test("rejects yesterday + tomorrow", () => {
    const now = new Date();
    const yesterday = new Date(now);
    yesterday.setDate(now.getDate() - 1);
    const tomorrow = new Date(now);
    tomorrow.setDate(now.getDate() + 1);
    expect(isToday(yesterday)).toBe(false);
    expect(isToday(tomorrow)).toBe(false);
  });
});
