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

const mockSafeGetJsonBody = vi.fn();
const mockValidateRequestBody = vi.fn();
const mockIsConvexEnabled = vi.fn();
const mockGetAuthedConvexClient = vi.fn();
const mockStartMnoteCliAgentHostRun = vi.fn();
const mockStartCodexJsonRun = vi.fn();
const mockCodexMessagesToPrompt = vi.fn();
const mockFindWorkspaceRoot = vi.fn();
const mockStartHermesRun = vi.fn();
const mockStreamHermesRunEvents = vi.fn();
const mockRequireAuthContext = vi.fn();
const mockGetConvexAuthedHttpClient = vi.fn();

vi.mock("next/server", () => ({
  NextResponse: class NextResponse extends Response {
    static json(body: unknown, init?: ResponseInit) {
      return new Response(JSON.stringify(body), {
        ...init,
        headers: {
          "Content-Type": "application/json",
          ...(init?.headers ?? {}),
        },
      });
    }
  },
}));

vi.mock("@/lib/api-utils", async () => {
  const actual = await vi.importActual<typeof import("@/lib/api-utils")>("@/lib/api-utils");
  return {
    ...actual,
    safeGetJsonBody: mockSafeGetJsonBody,
    validateRequestBody: mockValidateRequestBody,
  };
});

vi.mock("@/lib/convex/enabled", () => ({
  isConvexEnabled: mockIsConvexEnabled,
}));

vi.mock("@/lib/convex/route", () => ({
  getAuthedConvexClient: mockGetAuthedConvexClient,
}));

vi.mock("@/lib/auth/authContext", () => ({
  requireAuthContext: mockRequireAuthContext,
}));

vi.mock("@/lib/convex/server", () => ({
  getConvexAuthedHttpClient: mockGetConvexAuthedHttpClient,
}));

vi.mock("@/lib/server/mnote-cli-agent-host", () => ({
  startMnoteCliAgentHostRun: mockStartMnoteCliAgentHostRun,
}));

vi.mock("@/lib/ai/codex/codexExec", () => ({
  codexMessagesToPrompt: mockCodexMessagesToPrompt,
  findWorkspaceRoot: mockFindWorkspaceRoot,
  startCodexJsonRun: mockStartCodexJsonRun,
}));

vi.mock("@/lib/ai-agent/hermes/bridge", () => ({
  startHermesRun: mockStartHermesRun,
  streamHermesRunEvents: mockStreamHermesRunEvents,
}));

describe("/api/ai-agent/run route", () => {
  beforeEach(() => {
    mockSafeGetJsonBody.mockReset();
    mockValidateRequestBody.mockReset();
    mockIsConvexEnabled.mockReset();
    mockGetAuthedConvexClient.mockReset();
    mockStartMnoteCliAgentHostRun.mockReset();
    mockStartCodexJsonRun.mockReset();
    mockCodexMessagesToPrompt.mockReset();
    mockFindWorkspaceRoot.mockReset();
    mockStartHermesRun.mockReset();
    mockStreamHermesRunEvents.mockReset();
    mockRequireAuthContext.mockReset();
    mockGetConvexAuthedHttpClient.mockReset();
    mockCodexMessagesToPrompt.mockImplementation((messages) => messages.map((m: any) => m.content).join("\n"));
    mockFindWorkspaceRoot.mockResolvedValue("/mnt/Data1T/mnote");
  });

  it("文档页在线请求应只进入 mnote-cli host", async () => {
    mockSafeGetJsonBody.mockResolvedValue({
      stream: true,
      scope: "document",
      messages: [{ role: "user", content: "总结当前页面" }],
      context: {
        documentId: "doc-1",
        documentBlocks: [],
        pageOptions: {
          wideLayout: true,
        },
      },
      options: {
        ai: {
          provider: "online",
          sessionId: "doc_ai:doc-1:session-1",
          modelKey: "gpt-5.3-codex",
          profileId: "page_writer_polish",
        },
      },
    });
    mockValidateRequestBody.mockReturnValue(null);
    mockIsConvexEnabled.mockReturnValue(true);
    mockGetAuthedConvexClient.mockResolvedValue({
      auth: {
        userId: "user-1",
      },
    });
    mockRequireAuthContext.mockResolvedValue({
      userId: "user-1",
    });
    mockGetConvexAuthedHttpClient.mockResolvedValue({});
    mockStartMnoteCliAgentHostRun.mockResolvedValue(
      new Response('event: completion\ndata: {"ok":true,"text":"mnote-cli"}\n\n', {
        headers: {
          "Content-Type": "text/event-stream; charset=utf-8",
          "x-mnote-ai-execution-owner": "mnote-cli",
        },
      }),
    );

    const { POST } = await import("./route");
    const response = await POST(
      new Request("http://127.0.0.1:3000/api/ai-agent/run", {
        method: "POST",
      }),
    );
    const text = await response.text();

    expect(response.status).toBe(200);
    expect(mockStartMnoteCliAgentHostRun).toHaveBeenCalledWith(
      expect.objectContaining({
        userId: "user-1",
        payload: expect.objectContaining({
          scope: "document",
          context: expect.objectContaining({
            documentId: "doc-1",
            pageOptions: expect.objectContaining({
              wideLayout: true,
            }),
          }),
          options: expect.objectContaining({
            ai: expect.objectContaining({
              provider: "online",
              sessionId: "doc_ai:doc-1:session-1",
              modelKey: "gpt-5.3-codex",
              profileId: "page_writer_polish",
            }),
          }),
        }),
      }),
    );
    expect(text).toContain("mnote-cli");
  });

  it.each(["local", "ollama"] as const)(
    "provider=%s 继续进入 mnote-cli host",
    async (provider) => {
      mockSafeGetJsonBody.mockResolvedValue({
        stream: true,
        scope: "document",
        messages: [{ role: "user", content: "处理当前页面" }],
        context: {
          documentId: "doc-1",
        },
        options: {
          ai: {
            provider,
          },
        },
      });
      mockValidateRequestBody.mockReturnValue(null);
      mockIsConvexEnabled.mockReturnValue(true);
      mockGetAuthedConvexClient.mockResolvedValue({
        auth: {
          userId: "user-1",
        },
      });
      mockRequireAuthContext.mockResolvedValue({
        userId: "user-1",
      });
      mockGetConvexAuthedHttpClient.mockResolvedValue({});
      mockStartMnoteCliAgentHostRun.mockResolvedValue(
        new Response('event: completion\ndata: {"ok":true,"text":"mnote-cli"}\n\n', {
          headers: {
            "Content-Type": "text/event-stream; charset=utf-8",
            "x-mnote-ai-execution-owner": "mnote-cli",
          },
        }),
      );

      const { POST } = await import("./route");
      const response = await POST(
        new Request("http://127.0.0.1:3000/api/ai-agent/run", {
          method: "POST",
        }),
      );

      expect(response.status).toBe(200);
      expect(mockStartMnoteCliAgentHostRun).toHaveBeenCalledTimes(1);
      expect(mockStartMnoteCliAgentHostRun).toHaveBeenCalledWith(
        expect.objectContaining({
          payload: expect.objectContaining({
            options: expect.objectContaining({
              ai: expect.objectContaining({
                provider,
              }),
            }),
          }),
        }),
      );
    },
  );

  it.each(["codex", "hermes", "claudecode"] as const)(
    "provider=%s 已退场，必须明确失败且不能静默进入 mnote-cli",
    async (provider) => {
    mockSafeGetJsonBody.mockResolvedValue({
      stream: true,
      scope: "document",
      messages: [{ role: "user", content: "继续检查" }],
      context: {
        documentId: "doc-1",
      },
      options: {
        ai: {
          provider,
        },
      },
    });
    mockValidateRequestBody.mockReturnValue(null);
    mockIsConvexEnabled.mockReturnValue(true);
    mockGetAuthedConvexClient.mockResolvedValue({
      auth: {
        userId: "user-1",
      },
    });
    const { POST } = await import("./route");
    const response = await POST(new Request("http://127.0.0.1:3000/api/ai-agent/run", { method: "POST" }));
    const text = await response.text();

    expect(response.status).toBe(410);
    expect(mockStartMnoteCliAgentHostRun).not.toHaveBeenCalled();
    expect(response.headers.get("x-mnote-ai-execution-owner")).toBe(`${provider}-retired`);
    expect(mockStartCodexJsonRun).not.toHaveBeenCalled();
    expect(mockStartHermesRun).not.toHaveBeenCalled();
    expect(text).toContain(provider);
    },
  );

  it("未登录时不应启动 mnote-cli host", async () => {
    mockSafeGetJsonBody.mockResolvedValue({
      stream: true,
      messages: [{ role: "user", content: "总结当前页面" }],
    });
    mockValidateRequestBody.mockReturnValue(null);
    mockIsConvexEnabled.mockReturnValue(true);
    mockGetAuthedConvexClient.mockResolvedValue({
      auth: {
        userId: "",
      },
    });
    mockRequireAuthContext.mockRejectedValue(new Error("未登录，请先登录"));

    const { POST } = await import("./route");
    const response = await POST(
      new Request("http://127.0.0.1:3000/api/ai-agent/run", {
        method: "POST",
      }),
    );

    expect(response.status).toBe(401);
    expect(mockStartMnoteCliAgentHostRun).not.toHaveBeenCalled();
  });
});
