import { describe, expect, it } from "vite-plus/test";

import { TurnId } from "@t3tools/contracts";
import type { OrchestrationThreadActivity } from "@t3tools/contracts";

import {
  didLiveWindowReshape,
  freshOlderActivities,
  nextOlderActivitiesCursorRow,
  olderActivitiesCursorFor,
} from "./olderThreadActivities.ts";

const activity = (id: string, createdAt: string, sequence?: number): OrchestrationThreadActivity =>
  ({
    id,
    tone: "tool",
    kind: "command",
    summary: id,
    payload: {},
    turnId: TurnId.make("turn-1"),
    createdAt,
    ...(sequence !== undefined ? { sequence } : {}),
  }) as unknown as OrchestrationThreadActivity;

describe("didLiveWindowReshape", () => {
  const base = { key: "env thread-1", oldest: "a-1", count: 3 };

  it("a pure append (same key/oldest, count grows) is NOT a reshape", () => {
    expect(didLiveWindowReshape(base, { ...base, count: 4 })).toBe(false);
    expect(didLiveWindowReshape(base, base)).toBe(false);
  });

  it("a thread switch is a reshape", () => {
    expect(didLiveWindowReshape(base, { ...base, key: "env thread-2" })).toBe(true);
    expect(didLiveWindowReshape(base, { ...base, key: null })).toBe(true);
  });

  it("a reconnect re-snapshot (oldest row changes) is a reshape", () => {
    expect(didLiveWindowReshape(base, { ...base, oldest: "a-2" })).toBe(true);
    expect(didLiveWindowReshape(base, { ...base, oldest: null })).toBe(true);
  });

  it("a checkpoint revert (count shrinks) is a reshape", () => {
    expect(didLiveWindowReshape(base, { ...base, count: 2 })).toBe(true);
  });
});

describe("olderActivitiesCursorFor", () => {
  it("sequenced rows page by beforeSequence (including sequence 0)", () => {
    expect(olderActivitiesCursorFor(activity("a", "2026-04-01T10:00:00.000Z", 7))).toEqual({
      beforeSequence: 7,
    });
    expect(olderActivitiesCursorFor(activity("a", "2026-04-01T10:00:00.000Z", 0))).toEqual({
      beforeSequence: 0,
    });
  });

  it("unsequenced rows (the common case) page by the createdAt/id keyset", () => {
    expect(olderActivitiesCursorFor(activity("a-9", "2026-04-01T09:00:00.000Z"))).toEqual({
      beforeCreatedAt: "2026-04-01T09:00:00.000Z",
      beforeActivityId: "a-9",
    });
  });
});

describe("nextOlderActivitiesCursorRow", () => {
  const merged = [
    activity("newer", "2026-04-01T10:00:02.000Z", 2),
    activity("oldest", "2026-04-01T09:00:00.000Z"),
  ];

  it("prefers the explicit paged-past cursor so an all-overlap page keeps advancing", () => {
    const pagedPast = activity("paged-past", "2026-04-01T08:00:00.000Z");
    expect(nextOlderActivitiesCursorRow(pagedPast, merged)?.id).toBe("paged-past");
  });

  it("falls back to the chronologically-oldest loaded row, never index 0", () => {
    // The reducer sorts unsequenced rows to the end, so index 0 holds a NEWER
    // sequenced row here — cursoring from it would skip history.
    expect(nextOlderActivitiesCursorRow(null, merged)?.id).toBe("oldest");
    expect(nextOlderActivitiesCursorRow(null, [])).toBeNull();
  });
});

describe("freshOlderActivities", () => {
  const merged = [
    activity("known-1", "2026-04-01T09:00:00.000Z"),
    activity("known-2", "2026-04-01T10:00:00.000Z"),
  ];

  it("drops boundary-overlap rows already in the loaded set", () => {
    const page = {
      activities: [
        activity("older", "2026-04-01T08:00:00.000Z"),
        activity("known-1", "2026-04-01T09:00:00.000Z"),
      ],
      hasMore: true,
    };
    expect(freshOlderActivities(page, merged).map((entry) => entry.id)).toEqual(["older"]);
  });

  it("an all-overlap page yields no fresh rows (paging continues via the cursor)", () => {
    const page = { activities: merged, hasMore: true };
    expect(freshOlderActivities(page, merged)).toHaveLength(0);
  });
});
