import { describe, expect, test } from "bun:test";
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { CollectorState } from "../src/state";
import { Journal } from "../src/journal";
import { buildScheduler } from "../src/collector";
import { FixtureAdapter } from "./support/fixtureAdapter";
import { ManualClock } from "./support/manualClock";

function newState(clock: ManualClock): CollectorState {
  const dir = mkdtempSync(join(tmpdir(), "overdeck-sched-"));
  return new CollectorState(new Journal(join(dir, "items.jsonl")), () => clock.now());
}

describe("stale adapter marking", () => {
  test("adapter is fresh right after a successful poll", async () => {
    const clock = new ManualClock();
    const state = newState(clock);
    const adapter = new FixtureAdapter("fixture-fresh", 1000);
    const scheduler = buildScheduler([adapter], state, clock, () => 0.5);

    scheduler.start();
    await clock.advance(0);

    expect(state.isStale("fixture-fresh")).toBe(false);
    scheduler.stop();
  });

  test("adapter is marked stale once age exceeds 2x interval", async () => {
    const clock = new ManualClock();
    const state = newState(clock);
    const adapter = new FixtureAdapter("fixture-stale", 1000);
    const scheduler = buildScheduler([adapter], state, clock, () => 0);

    scheduler.start();
    await clock.advance(0);
    expect(state.isStale("fixture-stale")).toBe(false);

    // adapter now fails every subsequent poll; success timestamp stops advancing.
    adapter.poll = async () => {
      throw new Error("stuck");
    };

    await clock.advance(2100); // > 2 * interval since last success

    expect(state.isStale("fixture-stale")).toBe(true);
    scheduler.stop();
  });

  test("adapter that has never succeeded is stale", () => {
    const clock = new ManualClock();
    const state = newState(clock);
    state.registerAdapter("never-polled", 1000);
    expect(state.isStale("never-polled")).toBe(true);
  });

  test("throwing adapter never crashes the scheduler", async () => {
    const clock = new ManualClock();
    const state = newState(clock);
    const adapter = new FixtureAdapter("fixture-throws", 500);
    adapter.poll = async () => {
      throw new Error("boom");
    };
    const scheduler = buildScheduler([adapter], state, clock, () => 0);

    expect(() => scheduler.start()).not.toThrow();
    await clock.advance(0);
    await clock.advance(600);

    expect(state.adapterStatuses()[0]?.lastError).toBe("boom");
    scheduler.stop();
  });
});
