import { afterEach, describe, expect, it, vi } from "vitest";
import type { ScoutPost, ScoutRun } from "../domain/types";
import { scoutPostFixture } from "../test/scoutFixtures";
import { createLiveScout } from "./live/m5Repos";
import { createMockScoutRepo } from "./mock/scoutRepo";

function envelope(data: unknown): Response {
  return new Response(JSON.stringify({ code: 102000, message: "ok", data, error: null }), {
    status: 200,
    headers: { "Content-Type": "application/json" },
  });
}

function runFixture(): ScoutRun {
  return {
    id: "run-contract",
    job_id: "job-contract",
    theme_key: "activity|市集",
    theme_label: "市集",
    intent: "市集",
    mode: "activity",
    brand_id: null,
    target_count: 3,
    status: "succeeded",
    searched_count: 3,
    duplicate_count: 0,
    irrelevant_count: 0,
    eligible_count: 3,
    pending_count: 3,
    shortfall_count: 0,
    shortfall_reasons: [],
    created_at: 1_700_000_000_000_000_000,
  };
}

function postFixtures(runId: string): ScoutPost[] {
  return [
    scoutPostFixture({ id: "known-new", run_id: runId, posted_at: 300, created_at: 1 }),
    scoutPostFixture({ id: "known-old", run_id: runId, posted_at: 200, created_at: 2 }),
    scoutPostFixture({ id: "unknown", run_id: runId, posted_at: undefined, created_at: 3 }),
  ];
}

describe("ScoutRepo mock/live contract", () => {
  afterEach(() => vi.unstubAllGlobals());

  it("returns the same stable time ordering, page totals, and unknown-time semantics", async () => {
    const run = runFixture();
    const posts = postFixtures(run.id);
    const mock = createMockScoutRepo([{ run, posts }]);

    const fetchMock = vi.fn()
      .mockResolvedValueOnce(envelope({ run, list: posts.slice(0, 2), pagination: { page: 1, pageSize: 2, total: 3, totalPages: 2 } }))
      .mockResolvedValueOnce(envelope({ run, list: posts.slice(2), pagination: { page: 2, pageSize: 2, total: 3, totalPages: 2 } }));
    vi.stubGlobal("fetch", fetchMock);
    const live = createLiveScout();

    const [mockFirst, mockSecond, liveFirst, liveSecond] = await Promise.all([
      mock.listRunPosts(run.id, { page: 1, pageSize: 2 }),
      mock.listRunPosts(run.id, { page: 2, pageSize: 2 }),
      live.listRunPosts(run.id, { page: 1, pageSize: 2 }),
      live.listRunPosts(run.id, { page: 2, pageSize: 2 }),
    ]);
    expect(liveFirst.list.map((post) => post.id)).toEqual(mockFirst.list.map((post) => post.id));
    expect(liveSecond.list.map((post) => post.id)).toEqual(mockSecond.list.map((post) => post.id));
    expect(liveFirst.pagination).toEqual(mockFirst.pagination);
    expect(liveSecond.pagination).toEqual(mockSecond.pagination);
    expect(liveSecond.list[0]?.posted_at).toBeUndefined();
    expect(liveFirst.run.shortfall_reasons).toEqual(mockFirst.run.shortfall_reasons);
  });
});
