/**
 * Unit tests for dayChange helpers per TZ §4.1.17.
 *
 * Covers rules R1–R5:
 * R1/R2: computeDayChange returns B−A (local time date minus query date)
 * R3:    Badge hidden if result is 0; shown as +N/-N otherwise
 * R4:    Each time type is computed independently (demonstrated by calling
 *        computeDayChange separately for scheduled/expected/actual)
 * R5:    Tooltip is "День" for ±1, "DD.MM.YYYY" for ±2 or more
 */
import { describe, it, expect } from "vitest";
import {
  computeDayChange,
  dayChangeBadgeTooltip,
  formatDayChangeBadge,
} from "./dayChange.js";

// ---------------------------------------------------------------------------
// computeDayChange — TZ §4.1.17-R1, R2, R3, R4
// ---------------------------------------------------------------------------

describe("computeDayChange (TZ §4.1.17-R1–R4)", () => {
  it("returns 0 when time date equals query date (no badge — R3)", () => {
    expect(computeDayChange("2026-04-15T10:00:00", "2026-04-15")).toBe(0);
  });

  it("returns 0 when time date equals query date with TZ offset suffix", () => {
    expect(computeDayChange("2026-04-15T23:30:00+03:00", "2026-04-15")).toBe(0);
  });

  it("returns +1 when local date is the next day after query date", () => {
    expect(computeDayChange("2026-04-16T00:30:00", "2026-04-15")).toBe(1);
  });

  it("returns +1 with TZ offset-aware string crossing midnight", () => {
    // 2026-04-16T01:00:00+05:00 — local date is April 16 regardless of viewer TZ
    expect(computeDayChange("2026-04-16T01:00:00+05:00", "2026-04-15")).toBe(1);
  });

  it("returns +2 when two calendar days ahead", () => {
    expect(computeDayChange("2026-04-17T12:00:00", "2026-04-15")).toBe(2);
  });

  it("returns -1 when local date is one day before query date", () => {
    expect(computeDayChange("2026-04-14T23:00:00", "2026-04-15")).toBe(-1);
  });

  it("returns -2 when two calendar days before query date", () => {
    expect(computeDayChange("2026-04-13T08:00:00", "2026-04-15")).toBe(-2);
  });

  it("returns 0 for null input (guard)", () => {
    expect(computeDayChange(null, "2026-04-15")).toBe(0);
  });

  it("returns 0 for undefined input (guard)", () => {
    expect(computeDayChange(undefined, "2026-04-15")).toBe(0);
  });

  it("returns 0 for malformed time string", () => {
    expect(computeDayChange("not-a-date", "2026-04-15")).toBe(0);
  });

  it("returns 0 for malformed query date", () => {
    expect(computeDayChange("2026-04-16T10:00:00", "bad-date")).toBe(0);
  });

  it("returns 0 for empty time string", () => {
    expect(computeDayChange("", "2026-04-15")).toBe(0);
  });

  // R4: per-time-type independence — scheduled and actual may differ
  it("R4: scheduled and actual dayChange can differ independently", () => {
    const queryDate = "2026-04-15";
    const scheduledDayChange = computeDayChange(
      "2026-04-15T23:45:00+03:00",
      queryDate,
    );
    const actualDayChange = computeDayChange(
      "2026-04-16T00:30:00+03:00", // delayed past midnight
      queryDate,
    );
    expect(scheduledDayChange).toBe(0); // scheduled still April 15 → no badge
    expect(actualDayChange).toBe(1);    // actual crossed midnight → +1 badge
  });

  it("R4: estimated and actual can both differ from scheduled independently", () => {
    const queryDate = "2026-04-15";
    const scheduled = computeDayChange("2026-04-15T22:00:00", queryDate);
    const estimated = computeDayChange("2026-04-16T00:10:00", queryDate);
    const actual = computeDayChange("2026-04-16T00:45:00", queryDate);
    expect(scheduled).toBe(0);
    expect(estimated).toBe(1);
    expect(actual).toBe(1);
  });
});

// ---------------------------------------------------------------------------
// dayChangeBadgeTooltip — TZ §4.1.17-R5
// ---------------------------------------------------------------------------

describe("dayChangeBadgeTooltip (TZ §4.1.17-R5)", () => {
  it("returns 'День' for +1", () => {
    expect(dayChangeBadgeTooltip("2026-04-15T23:30:00", 1)).toBe("День");
  });

  it("returns 'День' for -1", () => {
    expect(dayChangeBadgeTooltip("2026-04-15T01:00:00", -1)).toBe("День");
  });

  it("returns 'DD.MM.YYYY' for +2", () => {
    // Base date is April 15 → +2 → April 17
    expect(dayChangeBadgeTooltip("2026-04-15T10:00:00", 2)).toBe("17.04.2026");
  });

  it("returns 'DD.MM.YYYY' for -2", () => {
    // Base date is April 15 → -2 → April 13
    expect(dayChangeBadgeTooltip("2026-04-15T10:00:00", -2)).toBe("13.04.2026");
  });

  it("handles month boundary correctly for +2", () => {
    // April 30 → +2 → May 2
    expect(dayChangeBadgeTooltip("2026-04-30T10:00:00", 2)).toBe("02.05.2026");
  });

  it("handles year boundary correctly", () => {
    // Dec 31 → +2 → Jan 2 next year
    expect(dayChangeBadgeTooltip("2026-12-31T10:00:00", 2)).toBe("02.01.2027");
  });

  it("handles offset-aware ISO string (uses local date from string)", () => {
    // Local date is April 15 (offset suffix doesn't change calendar date extraction)
    expect(dayChangeBadgeTooltip("2026-04-15T23:30:00+03:00", 2)).toBe(
      "17.04.2026",
    );
  });

  it("returns empty string for malformed base string", () => {
    expect(dayChangeBadgeTooltip("not-a-date", 2)).toBe("");
  });
});

// ---------------------------------------------------------------------------
// formatDayChangeBadge — TZ §4.1.17-R3
// ---------------------------------------------------------------------------

describe("formatDayChangeBadge (TZ §4.1.17-R3)", () => {
  it("returns '' for 0 (hidden)", () => {
    expect(formatDayChangeBadge(0)).toBe("");
  });

  it("returns '+1' for 1", () => {
    expect(formatDayChangeBadge(1)).toBe("+1");
  });

  it("returns '+2' for 2", () => {
    expect(formatDayChangeBadge(2)).toBe("+2");
  });

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

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