import { describe, test, expect } from "bun:test";
import {
  computeCacheTTL,
  TTL_HIGH_ACTIVITY,
  TTL_MODERATE_ACTIVITY,
  TTL_LOW_ACTIVITY,
} from "../../src/modules/algorithms/computeCacheTTL";

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

/** Fixed reference point so tests are deterministic. */
const NOW = new Date("2026-03-02T12:00:00Z");

/** Return a Date offset from NOW by `days` (negative = past, positive = future). */
const daysFromNow = (days: number): Date =>
  new Date(NOW.getTime() + days * 24 * 60 * 60 * 1000);

// ---------------------------------------------------------------------------
// Rule 1a — Closed records older than 30 days should not be cached
// ---------------------------------------------------------------------------
describe("computeCacheTTL — Rule 1a: Closed records (>30 days)", () => {
  test("returns null when closedFlag is true and closedDate is null", () => {
    expect(
      computeCacheTTL({
        closedFlag: true,
        closedDate: null,
        expectedCloseDate: null,
        lastUpdated: null,
        now: NOW,
      }),
    ).toBeNull();
  });

  test("returns null when closedFlag is true and closedDate is 60 days ago", () => {
    expect(
      computeCacheTTL({
        closedFlag: true,
        closedDate: daysFromNow(-60),
        expectedCloseDate: daysFromNow(-1),
        lastUpdated: daysFromNow(-1),
        now: NOW,
      }),
    ).toBeNull();
  });

  test("returns null when closedFlag is true and closedDate is 31 days ago", () => {
    expect(
      computeCacheTTL({
        closedFlag: true,
        closedDate: new Date(NOW.getTime() - 31 * 24 * 60 * 60 * 1000),
        expectedCloseDate: daysFromNow(2),
        lastUpdated: null,
        now: NOW,
      }),
    ).toBeNull();
  });
});

// ---------------------------------------------------------------------------
// Rule 1b — Recently closed (within 30 days) → 15 minutes
// ---------------------------------------------------------------------------
describe("computeCacheTTL — Rule 1b: Recently closed (≤30 days)", () => {
  test("returns 15min when closed 1 day ago", () => {
    expect(
      computeCacheTTL({
        closedFlag: true,
        closedDate: daysFromNow(-1),
        expectedCloseDate: null,
        lastUpdated: null,
        now: NOW,
      }),
    ).toBe(TTL_LOW_ACTIVITY);
  });

  test("returns 15min when closed 15 days ago", () => {
    expect(
      computeCacheTTL({
        closedFlag: true,
        closedDate: daysFromNow(-15),
        expectedCloseDate: null,
        lastUpdated: null,
        now: NOW,
      }),
    ).toBe(TTL_LOW_ACTIVITY);
  });

  test("returns 15min when closed exactly 30 days ago", () => {
    expect(
      computeCacheTTL({
        closedFlag: true,
        closedDate: daysFromNow(-30),
        expectedCloseDate: null,
        lastUpdated: null,
        now: NOW,
      }),
    ).toBe(TTL_LOW_ACTIVITY);
  });

  test("returns 15min when closed today even with recent activity dates", () => {
    expect(
      computeCacheTTL({
        closedFlag: true,
        closedDate: NOW,
        expectedCloseDate: daysFromNow(-1),
        lastUpdated: NOW,
        now: NOW,
      }),
    ).toBe(TTL_LOW_ACTIVITY);
  });

  test("just past 30-day boundary returns null", () => {
    const justPast30Days = new Date(
      NOW.getTime() - 30 * 24 * 60 * 60 * 1000 - 1,
    );
    expect(
      computeCacheTTL({
        closedFlag: true,
        closedDate: justPast30Days,
        expectedCloseDate: null,
        lastUpdated: null,
        now: NOW,
      }),
    ).toBeNull();
  });
});

// ---------------------------------------------------------------------------
// Rule 2 — High activity (within 5 days) → 30 seconds
// ---------------------------------------------------------------------------
describe("computeCacheTTL — Rule 2: High activity (≤5 days)", () => {
  test("returns 30s when lastUpdated is today", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: null,
        lastUpdated: NOW,
        now: NOW,
      }),
    ).toBe(TTL_HIGH_ACTIVITY);
  });

  test("returns 30s when lastUpdated is 3 days ago", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: null,
        lastUpdated: daysFromNow(-3),
        now: NOW,
      }),
    ).toBe(TTL_HIGH_ACTIVITY);
  });

  test("returns 30s when lastUpdated is exactly 5 days ago", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: null,
        lastUpdated: daysFromNow(-5),
        now: NOW,
      }),
    ).toBe(TTL_HIGH_ACTIVITY);
  });

  test("returns 30s when expectedCloseDate is 2 days in the future", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: daysFromNow(2),
        lastUpdated: null,
        now: NOW,
      }),
    ).toBe(TTL_HIGH_ACTIVITY);
  });

  test("returns 30s when expectedCloseDate is 5 days in the future", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: daysFromNow(5),
        lastUpdated: null,
        now: NOW,
      }),
    ).toBe(TTL_HIGH_ACTIVITY);
  });

  test("returns 30s when expectedCloseDate is 4 days ago (recently passed)", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: daysFromNow(-4),
        lastUpdated: null,
        now: NOW,
      }),
    ).toBe(TTL_HIGH_ACTIVITY);
  });

  test("returns 30s when either date is within 5 days (lastUpdated wins)", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: daysFromNow(-30),
        lastUpdated: daysFromNow(-2),
        now: NOW,
      }),
    ).toBe(TTL_HIGH_ACTIVITY);
  });

  test("returns 30s when either date is within 5 days (expectedCloseDate wins)", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: daysFromNow(3),
        lastUpdated: daysFromNow(-30),
        now: NOW,
      }),
    ).toBe(TTL_HIGH_ACTIVITY);
  });
});

// ---------------------------------------------------------------------------
// Rule 3 — Moderate activity (within 14 days but > 5 days) → 60 seconds
// ---------------------------------------------------------------------------
describe("computeCacheTTL — Rule 3: Moderate activity (6–14 days)", () => {
  test("returns 60s when lastUpdated is 6 days ago", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: null,
        lastUpdated: daysFromNow(-6),
        now: NOW,
      }),
    ).toBe(TTL_MODERATE_ACTIVITY);
  });

  test("returns 60s when lastUpdated is 10 days ago", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: null,
        lastUpdated: daysFromNow(-10),
        now: NOW,
      }),
    ).toBe(TTL_MODERATE_ACTIVITY);
  });

  test("returns 60s when lastUpdated is exactly 14 days ago", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: null,
        lastUpdated: daysFromNow(-14),
        now: NOW,
      }),
    ).toBe(TTL_MODERATE_ACTIVITY);
  });

  test("returns 60s when expectedCloseDate is 8 days in the future", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: daysFromNow(8),
        lastUpdated: null,
        now: NOW,
      }),
    ).toBe(TTL_MODERATE_ACTIVITY);
  });

  test("returns 60s when expectedCloseDate is 14 days in the future", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: daysFromNow(14),
        lastUpdated: null,
        now: NOW,
      }),
    ).toBe(TTL_MODERATE_ACTIVITY);
  });

  test("returns 60s when expectedCloseDate is 12 days ago", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: daysFromNow(-12),
        lastUpdated: null,
        now: NOW,
      }),
    ).toBe(TTL_MODERATE_ACTIVITY);
  });
});

// ---------------------------------------------------------------------------
// Rule 4 — Low activity (older than 14 days) → 15 minutes
// ---------------------------------------------------------------------------
describe("computeCacheTTL — Rule 4: Low activity (>14 days)", () => {
  test("returns 15min when lastUpdated is 15 days ago", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: null,
        lastUpdated: daysFromNow(-15),
        now: NOW,
      }),
    ).toBe(TTL_LOW_ACTIVITY);
  });

  test("returns 15min when lastUpdated is 60 days ago", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: null,
        lastUpdated: daysFromNow(-60),
        now: NOW,
      }),
    ).toBe(TTL_LOW_ACTIVITY);
  });

  test("returns 15min when expectedCloseDate is 20 days in the future", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: daysFromNow(20),
        lastUpdated: null,
        now: NOW,
      }),
    ).toBe(TTL_LOW_ACTIVITY);
  });

  test("returns 15min when both dates are null", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: null,
        lastUpdated: null,
        now: NOW,
      }),
    ).toBe(TTL_LOW_ACTIVITY);
  });

  test("returns 15min when both dates are far in the past", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: daysFromNow(-100),
        lastUpdated: daysFromNow(-90),
        now: NOW,
      }),
    ).toBe(TTL_LOW_ACTIVITY);
  });
});

// ---------------------------------------------------------------------------
// Edge cases
// ---------------------------------------------------------------------------
describe("computeCacheTTL — edge cases", () => {
  test("defaults `now` to current time when omitted", () => {
    // Open, no dates → should return LOW_ACTIVITY (15min)
    const result = computeCacheTTL({
      closedFlag: false,
      closedDate: null,
      expectedCloseDate: null,
      lastUpdated: null,
    });
    expect(result).toBe(TTL_LOW_ACTIVITY);
  });

  test("5-day boundary is inclusive", () => {
    // Exactly 5 days should match high activity
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: null,
        lastUpdated: daysFromNow(-5),
        now: NOW,
      }),
    ).toBe(TTL_HIGH_ACTIVITY);
  });

  test("just past 5-day boundary falls to moderate", () => {
    // 5 days + 1 millisecond past → moderate
    const justPast5Days = new Date(NOW.getTime() - 5 * 24 * 60 * 60 * 1000 - 1);
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: null,
        lastUpdated: justPast5Days,
        now: NOW,
      }),
    ).toBe(TTL_MODERATE_ACTIVITY);
  });

  test("14-day boundary is inclusive", () => {
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: null,
        lastUpdated: daysFromNow(-14),
        now: NOW,
      }),
    ).toBe(TTL_MODERATE_ACTIVITY);
  });

  test("just past 14-day boundary falls to low activity", () => {
    const justPast14Days = new Date(
      NOW.getTime() - 14 * 24 * 60 * 60 * 1000 - 1,
    );
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: null,
        lastUpdated: justPast14Days,
        now: NOW,
      }),
    ).toBe(TTL_LOW_ACTIVITY);
  });

  test("higher-priority rule wins when both dates span different tiers", () => {
    // expectedCloseDate in 5-day window, lastUpdated in 14-day window → 30s
    expect(
      computeCacheTTL({
        closedFlag: false,
        closedDate: null,
        expectedCloseDate: daysFromNow(3),
        lastUpdated: daysFromNow(-10),
        now: NOW,
      }),
    ).toBe(TTL_HIGH_ACTIVITY);
  });

  test("closed >30 days always returns null regardless of other dates", () => {
    expect(
      computeCacheTTL({
        closedFlag: true,
        closedDate: daysFromNow(-60),
        expectedCloseDate: NOW,
        lastUpdated: NOW,
        now: NOW,
      }),
    ).toBeNull();
  });

  test("recently closed always returns 15min regardless of activity dates", () => {
    expect(
      computeCacheTTL({
        closedFlag: true,
        closedDate: daysFromNow(-5),
        expectedCloseDate: NOW,
        lastUpdated: NOW,
        now: NOW,
      }),
    ).toBe(TTL_LOW_ACTIVITY);
  });
});
