import { afterEach, expect, test, vi } from "vitest";
import { apiRequest, setAccessToken, setRefreshHandler, setUnauthorizedHandler } from "./request";

afterEach(() => {
  setAccessToken("");
  setRefreshHandler(null);
  setUnauthorizedHandler(null);
  vi.unstubAllGlobals();
});

test("retries once after refreshing a stale permission token on 403", async () => {
  setAccessToken("old-token");
  setRefreshHandler(async () => {
    setAccessToken("new-token");
    return true;
  });
  vi.stubGlobal(
    "fetch",
    vi.fn(async () => {
      if (vi.mocked(fetch).mock.calls.length === 1) {
        return new Response(JSON.stringify({ code: 403, message: "没有操作权限" }), { status: 403 });
      }
      return new Response(JSON.stringify({ code: 0, data: { ok: true } }));
    })
  );

  await expect(apiRequest("/v1/admin/countries", { body: {}, method: "POST" })).resolves.toEqual({ ok: true });

  expect(fetch).toHaveBeenCalledTimes(2);
  expect(vi.mocked(fetch).mock.calls[0][1]?.headers).toMatchObject({ Authorization: "Bearer old-token" });
  expect(vi.mocked(fetch).mock.calls[1][1]?.headers).toMatchObject({ Authorization: "Bearer new-token" });
});

test("shares one refresh request across concurrent stale access token responses", async () => {
  setAccessToken("old-token");
  let refreshCount = 0;
  setRefreshHandler(async () => {
    refreshCount += 1;
    await new Promise((resolve) => window.setTimeout(resolve, 0));
    setAccessToken("new-token");
    return true;
  });
  vi.stubGlobal(
    "fetch",
    vi.fn(async (_url, init) => {
      const auth = (init?.headers as Record<string, string> | undefined)?.Authorization;
      if (auth === "Bearer old-token") {
        return new Response(JSON.stringify({ code: 40100, message: "访问凭证已失效" }), { status: 401 });
      }
      return new Response(JSON.stringify({ code: 0, data: { ok: true } }));
    })
  );

  await expect(Promise.all([apiRequest("/v1/admin/countries"), apiRequest("/v1/admin/regions")])).resolves.toEqual([
    { ok: true },
    { ok: true }
  ]);

  expect(refreshCount).toBe(1);
  expect(fetch).toHaveBeenCalledTimes(4);
});
