import { describe, it, expect } from "vitest";
import {
  getMinDate,
  getMaxDate,
  buildDisabledDates,
  findNextEnabledDate,
} from "./calendarRange.js";

function yyyymmdd(d: Date): string {
  const y = d.getFullYear().toString();
  const m = (d.getMonth() + 1).toString().padStart(2, "0");
  const day = d.getDate().toString().padStart(2, "0");
  return `${y}${m}${day}`;
}

function addDays(d: Date, n: number): Date {
  const x = new Date(d);
  x.setDate(x.getDate() + n);
  x.setHours(0, 0, 0, 0);
  return x;
}

describe("getMinDate / getMaxDate", () => {
  it("minDate is today - 1 day, time zero'd", () => {
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    const expected = addDays(today, -1);
    const min = getMinDate();
    expect(min.getTime()).toBe(expected.getTime());
    expect(min.getHours()).toBe(0);
    expect(min.getMinutes()).toBe(0);
    expect(min.getSeconds()).toBe(0);
  });

  it("maxDate is today + 6 months, time zero'd", () => {
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    const expected = new Date(today);
    expected.setMonth(expected.getMonth() + 6);
    const max = getMaxDate();
    expect(max.getTime()).toBe(expected.getTime());
  });
});

describe("buildDisabledDates", () => {
  const min = new Date(2026, 0, 1);
  const max = new Date(2026, 0, 5);

  it("returns every day in the window when availableDays is empty", () => {
    const disabled = buildDisabledDates(min, max, []);
    expect(disabled.map((d) => d.getDate())).toEqual([1, 2, 3, 4, 5]);
  });

  it("returns [] when all dates in window are available", () => {
    const available = ["20260101", "20260102", "20260103", "20260104", "20260105"];
    expect(buildDisabledDates(min, max, available)).toEqual([]);
  });

  it("returns the exact complement for partial availability", () => {
    const available = ["20260102", "20260104"];
    const disabled = buildDisabledDates(min, max, available);
    expect(disabled.map((d) => d.getDate())).toEqual([1, 3, 5]);
  });

  it("ignores availableDays entries outside the window", () => {
    const available = ["20251231", "20260601", "20260103"];
    const disabled = buildDisabledDates(min, max, available);
    expect(disabled.map((d) => d.getDate())).toEqual([1, 2, 4, 5]);
  });
});

describe("findNextEnabledDate", () => {
  const min = new Date(2026, 0, 1);
  const max = new Date(2026, 0, 5);

  it("returns current when current is enabled", () => {
    const current = new Date(2026, 0, 3);
    const disabled = [new Date(2026, 0, 1), new Date(2026, 0, 2)];
    const result = findNextEnabledDate(current, disabled, min, max);
    expect(result?.getDate()).toBe(3);
  });

  it("advances to the next enabled date when current is disabled", () => {
    const current = new Date(2026, 0, 2);
    const disabled = [new Date(2026, 0, 1), new Date(2026, 0, 2), new Date(2026, 0, 3)];
    const result = findNextEnabledDate(current, disabled, min, max);
    expect(result?.getDate()).toBe(4);
  });

  it("returns null when every date in window is disabled", () => {
    const current = new Date(2026, 0, 3);
    const disabled = [
      new Date(2026, 0, 1),
      new Date(2026, 0, 2),
      new Date(2026, 0, 3),
      new Date(2026, 0, 4),
      new Date(2026, 0, 5),
    ];
    expect(findNextEnabledDate(current, disabled, min, max)).toBeNull();
  });

  it("starts at minDate when current < minDate", () => {
    const current = new Date(2025, 11, 25);
    const disabled: Date[] = [];
    const result = findNextEnabledDate(current, disabled, min, max);
    expect(result?.getDate()).toBe(1);
  });

  it("returns null when current > maxDate", () => {
    const current = new Date(2026, 1, 1);
    const disabled: Date[] = [];
    expect(findNextEnabledDate(current, disabled, min, max)).toBeNull();
  });
});
