import { renderHook, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import type { Post } from "../../../types/post";
import { usePostStream } from "./usePostStream";

function jsonResponse(body: unknown) {
  return new Response(JSON.stringify(body), {
    status: 200,
    headers: { "Content-Type": "application/json" },
  });
}

function post(id: string): Post {
  return {
    id,
    categoryId: 1,
    categorySlug: "general",
    language: "zh-CN",
    attachments: [],
    isRecommended: false,
    publishedAt: "2026-05-31T00:00:00.000Z",
  };
}

const params = {
  scope: { kind: "all" as const },
  type: "all",
  q: "",
  lang: "zh-CN" as const,
};

describe("usePostStream", () => {
  afterEach(() => {
    vi.restoreAllMocks();
    vi.unstubAllGlobals();
  });

  it("expires the in-memory stream cache", async () => {
    const now = vi.spyOn(Date, "now").mockReturnValue(1000);
    const fetchMock = vi
      .fn()
      .mockResolvedValueOnce(jsonResponse({ items: [post("fresh")] }))
      .mockResolvedValueOnce(jsonResponse({ items: [post("refetched")] }));
    vi.stubGlobal("fetch", fetchMock);

    const first = renderHook(() => usePostStream(params));
    await waitFor(() =>
      expect(first.result.current.items[0]?.id).toBe("fresh"),
    );
    first.unmount();

    now.mockReturnValue(1000 + 60 * 1000);
    const cached = renderHook(() => usePostStream(params));
    expect(cached.result.current.items[0]?.id).toBe("fresh");
    expect(cached.result.current.isLoading).toBe(false);
    expect(fetchMock).toHaveBeenCalledTimes(1);
    cached.unmount();

    now.mockReturnValue(1000 + 5 * 60 * 1000 + 1);
    const expired = renderHook(() => usePostStream(params));
    await waitFor(() =>
      expect(expired.result.current.items[0]?.id).toBe("refetched"),
    );
    expect(fetchMock).toHaveBeenCalledTimes(2);
    expired.unmount();
  });
});
