import { describe, it, expect } from "vitest";
import {
  previousMonth,
  defaultReferencePeriod,
  comparisonMeta,
  syncReferenceOnPeriodChange,
} from "./useCompare";

describe("useCompare helpers", () => {
  describe("previousMonth", () => {
    it("goes back one month within the same year", () => {
      expect(previousMonth(2026, 3)).toEqual({ year: 2026, month: 2 });
      expect(previousMonth(2026, 12)).toEqual({ year: 2026, month: 11 });
    });

    it("wraps around January to December of previous year", () => {
      expect(previousMonth(2026, 1)).toEqual({ year: 2025, month: 12 });
    });
  });

  describe("defaultReferencePeriod", () => {
    it("returns the month before the given date", () => {
      expect(defaultReferencePeriod(new Date(2026, 3, 15))).toEqual({ year: 2026, month: 3 });
    });

    it("wraps around when today is in January", () => {
      expect(defaultReferencePeriod(new Date(2026, 0, 10))).toEqual({ year: 2025, month: 12 });
    });

    it("handles the last day of a month", () => {
      expect(defaultReferencePeriod(new Date(2026, 6, 31))).toEqual({ year: 2026, month: 6 });
    });
  });

  describe("comparisonMeta", () => {
    it("MoM returns the previous month", () => {
      expect(comparisonMeta("mom", 2026, 3)).toEqual({ previousYear: 2026, previousMonth: 2 });
    });

    it("MoM wraps around January", () => {
      expect(comparisonMeta("mom", 2026, 1)).toEqual({ previousYear: 2025, previousMonth: 12 });
    });

    it("YoY returns the same month in the previous year", () => {
      expect(comparisonMeta("yoy", 2026, 3)).toEqual({ previousYear: 2025, previousMonth: 3 });
    });

    it("YoY for January stays on January of previous year", () => {
      expect(comparisonMeta("yoy", 2026, 1)).toEqual({ previousYear: 2025, previousMonth: 1 });
    });
  });

  describe("syncReferenceOnPeriodChange", () => {
    // The ref is seeded with the initial `to`, so the mount call has to===last.
    it("no-ops on mount, preserving the previous-month default", () => {
      const r = syncReferenceOnPeriodChange("2026-12-31", "2026-12-31", 2026, 6);
      expect(r.next).toBeNull();
      expect(r.lastSyncedTo).toBe("2026-12-31");
    });

    // StrictMode double-invokes effects in dev: calling twice with the same `to`
    // must not sync (a fire-once boolean would wrongly snap to December on run 2).
    it("is idempotent across a repeated `to` (StrictMode-safe)", () => {
      const a = syncReferenceOnPeriodChange("2026-12-31", "2026-12-31", 2026, 6);
      const b = syncReferenceOnPeriodChange(a.lastSyncedTo, "2026-12-31", 2026, 6);
      expect(a.next).toBeNull();
      expect(b.next).toBeNull();
    });

    it("syncs the reference month when `to` changes (user navigation)", () => {
      const r = syncReferenceOnPeriodChange("2026-12-31", "2026-06-30", 2026, 12);
      expect(r.next).toEqual({ year: 2026, month: 6 });
      expect(r.lastSyncedTo).toBe("2026-06-30");
    });

    it("records a changed `to` but does not dispatch when it already matches state", () => {
      const r = syncReferenceOnPeriodChange("2026-12-31", "2026-06-30", 2026, 6);
      expect(r.next).toBeNull();
      expect(r.lastSyncedTo).toBe("2026-06-30");
    });

    it("ignores an unparseable `to`", () => {
      const r = syncReferenceOnPeriodChange("2026-12-31", "garbage", 2026, 6);
      expect(r.next).toBeNull();
      expect(r.lastSyncedTo).toBe("garbage");
    });
  });
});
