import { beforeEach, describe, expect, it, vi } from "vitest";

const mockResolveMnoteWebInternalUrl = vi.fn();
const mockBuildForwardHeaders = vi.fn();
const mockFetch = vi.fn();

vi.mock("@/lib/mnote-web/internal-url", () => ({
  resolveMnoteWebInternalUrl: (...args: unknown[]) => mockResolveMnoteWebInternalUrl(...args),
}));

vi.mock("@/lib/server/forward-headers", () => ({
  buildForwardHeaders: (...args: unknown[]) => mockBuildForwardHeaders(...args),
}));

describe("/api/tree/runtime/reduce route", () => {
  beforeEach(() => {
    vi.resetModules();
    mockResolveMnoteWebInternalUrl.mockReset();
    mockBuildForwardHeaders.mockReset();
    mockFetch.mockReset();
    vi.stubGlobal("fetch", mockFetch);
  });

  it("通过 3000 同源 POST 代理到 mnote-web runtime reduce，并原样转发 JSON body 与来源 header", async () => {
    mockResolveMnoteWebInternalUrl.mockResolvedValue("http://127.0.0.1:3104");
    mockBuildForwardHeaders.mockResolvedValue(
      new Headers({
        cookie: "session=abc",
        authorization: "Bearer test-token",
      }),
    );
    mockFetch.mockResolvedValue(
      new Response(JSON.stringify({ ok: true, revision: 7 }), {
        status: 200,
        statusText: "OK",
        headers: {
          "content-type": "application/json; charset=utf-8",
          "set-cookie": "debug=1",
          connection: "keep-alive",
          "transfer-encoding": "chunked",
          "x-upstream": "mnote-web-runtime",
        },
      }),
    );

    const { POST } = await import("./route");
    const body = JSON.stringify({
      workspaceId: "ws_body",
      op: "tree.node.rename",
      payload: { documentId: "doc_1", title: "新标题" },
    });
    const response = await POST(
      new Request("http://127.0.0.1:3000/api/tree/runtime/reduce?workspaceId=ws_query", {
        method: "POST",
        headers: {
          "content-type": "application/json",
          accept: "application/json",
          cookie: "session=abc",
        },
        body,
      }),
    );

    expect(mockFetch).toHaveBeenCalledWith(
      "http://127.0.0.1:3104/api/tree/runtime/reduce?workspaceId=ws_query",
      expect.objectContaining({
        method: "POST",
        headers: expect.any(Headers),
        body,
        cache: "no-store",
        redirect: "manual",
      }),
    );

    const fetchHeaders = mockFetch.mock.calls[0]?.[1]?.headers as Headers;
    expect(fetchHeaders.get("cookie")).toBe("session=abc");
    expect(fetchHeaders.get("authorization")).toBe("Bearer test-token");
    expect(fetchHeaders.get("content-type")).toBe("application/json");
    expect(fetchHeaders.get("accept")).toBe("application/json");
    expect(fetchHeaders.get("x-mnote-source-channel")).toBe("next_tree_runtime_reduce_proxy");
    expect(fetchHeaders.get("x-mnote-source-client")).toBe("wolai-frontend");
    expect(fetchHeaders.get("x-mnote-workspace-id")).toBe("ws_query");

    expect(response.status).toBe(200);
    expect(response.headers.get("content-type")).toContain("application/json");
    expect(response.headers.get("cache-control")).toBe("no-store");
    expect(response.headers.get("set-cookie")).toBeNull();
    expect(response.headers.get("connection")).toBeNull();
    expect(response.headers.get("transfer-encoding")).toBeNull();
    expect(response.headers.get("x-upstream")).toBe("mnote-web-runtime");
    expect(await response.json()).toEqual({ ok: true, revision: 7 });
  });

  it("没有 workspaceId query 时使用 x-mnote-workspace-id header fallback", async () => {
    mockResolveMnoteWebInternalUrl.mockResolvedValue("http://127.0.0.1:3104");
    mockBuildForwardHeaders.mockResolvedValue(
      new Headers({
        "x-mnote-workspace-id": "ws_header",
      }),
    );
    mockFetch.mockResolvedValue(
      new Response(JSON.stringify({ ok: true }), {
        status: 202,
        headers: {
          "content-type": "application/json",
        },
      }),
    );

    const { POST } = await import("./route");
    await POST(
      new Request("http://127.0.0.1:3000/api/tree/runtime/reduce", {
        method: "POST",
        headers: {
          "content-type": "application/json",
          "x-mnote-workspace-id": "ws_header",
        },
        body: JSON.stringify({ op: "noop" }),
      }),
    );

    expect(mockFetch).toHaveBeenCalledWith(
      "http://127.0.0.1:3104/api/tree/runtime/reduce",
      expect.objectContaining({
        method: "POST",
      }),
    );
    const fetchHeaders = mockFetch.mock.calls[0]?.[1]?.headers as Headers;
    expect(fetchHeaders.get("x-mnote-workspace-id")).toBe("ws_header");
  });

  it("上游调用异常时返回 502 JSON", async () => {
    mockResolveMnoteWebInternalUrl.mockResolvedValue("http://127.0.0.1:3104");
    mockBuildForwardHeaders.mockResolvedValue(new Headers());
    mockFetch.mockRejectedValue(new Error("runtime reduce unavailable"));

    const { POST } = await import("./route");
    const response = await POST(
      new Request("http://127.0.0.1:3000/api/tree/runtime/reduce", {
        method: "POST",
        headers: {
          "content-type": "application/json",
        },
        body: JSON.stringify({ op: "noop" }),
      }),
    );

    expect(response.status).toBe(502);
    expect(response.headers.get("cache-control")).toBe("no-store");
    expect(await response.json()).toEqual({
      error: "runtime reduce unavailable",
    });
  });
});
