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 mockStartHermesRun = vi.fn();
const mockStreamHermesRunEvents = vi.fn();
const mockBuildDocumentBridgeContext = vi.fn();
const mockExecuteRustBridgeTool = vi.fn();
const mockStartDocumentAiOrchestratorRun = vi.fn();

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/ai-agent/hermes/bridge", () => ({
  startHermesRun: mockStartHermesRun,
  streamHermesRunEvents: mockStreamHermesRunEvents,
}));

vi.mock("@/lib/documents/bridge", () => ({
  buildDocumentBridgeContext: mockBuildDocumentBridgeContext,
}));

vi.mock("@/lib/documents/rust-runtime", () => ({
  executeRustBridgeTool: mockExecuteRustBridgeTool,
}));

vi.mock("@/lib/server/document-ai-orchestrator", () => ({
  startDocumentAiOrchestratorRun: mockStartDocumentAiOrchestratorRun,
}));

describe("/api/ai-agent/run route", () => {
  beforeEach(() => {
    mockSafeGetJsonBody.mockReset();
    mockValidateRequestBody.mockReset();
    mockIsConvexEnabled.mockReset();
    mockGetAuthedConvexClient.mockReset();
    mockStartHermesRun.mockReset();
    mockStreamHermesRunEvents.mockReset();
    mockBuildDocumentBridgeContext.mockReset();
    mockExecuteRustBridgeTool.mockReset();
    mockStartDocumentAiOrchestratorRun.mockReset();
  });

  it("应把 Hermes slash_run 完成事件恢复成结构化 tool_result", async () => {
    mockSafeGetJsonBody.mockResolvedValue({
      stream: true,
      scope: "document",
      messages: [{ role: "user", content: "把标题改成 AI 标题" }],
      context: {
        documentId: "doc-1",
      },
      options: {
        ai: {
          provider: "hermes",
        },
      },
    });
    mockValidateRequestBody.mockReturnValue(null);
    mockIsConvexEnabled.mockReturnValue(true);
    mockGetAuthedConvexClient.mockResolvedValue({
      auth: {
        userId: "user-1",
      },
      client: { query: vi.fn(), mutation: vi.fn() },
    });
    mockBuildDocumentBridgeContext.mockResolvedValue({
      deploymentId: null,
      projectId: null,
      workspaceId: null,
      requestId: "req-1",
      traceId: "trace-1",
      actor: {
        actorType: "user",
        actorId: "user-1",
        sessionId: null,
      },
      source: {
        channel: "next-route",
        client: "wolai-frontend",
      },
      tenantId: null,
      authToken: null,
      idempotencyKey: null,
      validateOnly: false,
      dryRun: false,
    });
    mockStartHermesRun.mockResolvedValue({
      runId: "run-1",
    });
    mockStreamHermesRunEvents.mockImplementation(async (_runId, onEvent) => {
      await onEvent({
        event: "tool.started",
        tool: "slash_run",
        preview: '{"text":"/rename doc-1 AI 标题"}',
      });
      await onEvent({
        event: "tool.completed",
        tool: "slash_run",
        duration: 0.12,
        error: false,
      });
      await onEvent({
        event: "run.completed",
        output: "已完成",
      });
    });
    mockExecuteRustBridgeTool.mockResolvedValue({
      plan: {
        kind: "tool",
        toolName: "slash_run",
      },
      result: {
        ok: true,
        parsed: {
          command: "rename_doc",
          params: {
            documentId: "doc-1",
            title: "AI 标题",
          },
        },
      },
    });

    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(mockExecuteRustBridgeTool).toHaveBeenCalledWith(
      expect.objectContaining({
        toolName: "slash_run",
        args: {
          text: "/rename doc-1 AI 标题",
        },
        data: {
          source: "ai-agent-route",
        },
      }),
    );
    expect(text).toContain("event: tool_result");
    expect(text).toContain('"tool":"slash_run"');
    expect(text).toContain('"command":"rename_doc"');
    expect(text).toContain('"title":"AI 标题"');
  });

  it("文档页 AI 请求应把 pageOptions 带入 Hermes instructions", async () => {
    mockSafeGetJsonBody.mockResolvedValue({
      stream: true,
      scope: "document",
      messages: [{ role: "user", content: "根据当前页面设置调整内容" }],
      context: {
        documentId: "doc-1",
        pageOptions: {
          wideLayout: true,
          smallText: true,
          layoutDensity: "compact",
        },
      },
      options: {
        ai: {
          provider: "hermes",
        },
      },
    });
    mockValidateRequestBody.mockReturnValue(null);
    mockIsConvexEnabled.mockReturnValue(true);
    mockGetAuthedConvexClient.mockResolvedValue({
      auth: {
        userId: "user-1",
      },
    });
    mockStartHermesRun.mockResolvedValue({
      runId: "run-2",
    });
    mockStreamHermesRunEvents.mockImplementation(async (_runId, onEvent) => {
      await onEvent({
        event: "run.completed",
        output: "已完成",
      });
    });

    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(mockStartHermesRun).toHaveBeenCalledWith(
      expect.objectContaining({
        instructions: expect.stringContaining("pageOptions="),
      }),
    );
    expect(mockStartHermesRun).toHaveBeenCalledWith(
      expect.objectContaining({
        instructions: expect.stringContaining('"wideLayout":true'),
      }),
    );
    expect(mockStartHermesRun).toHaveBeenCalledWith(
      expect.objectContaining({
        instructions: expect.stringContaining("editorRuntimePageOptions="),
      }),
    );
  });

  it("文档页在线流式请求应优先走 orchestrator", async () => {
    mockSafeGetJsonBody.mockResolvedValue({
      stream: true,
      scope: "document",
      messages: [{ role: "user", content: "总结当前页面" }],
      context: {
        documentId: "doc-1",
        documentBlocks: [],
      },
      options: {
        ai: {
          provider: "online",
        },
      },
    });
    mockValidateRequestBody.mockReturnValue(null);
    mockIsConvexEnabled.mockReturnValue(true);
    mockGetAuthedConvexClient.mockResolvedValue({
      auth: {
        userId: "user-1",
      },
    });
    mockStartDocumentAiOrchestratorRun.mockResolvedValue(
      new Response(
        'event: assistant_message\ndata: {"text":"来自 orchestrator"}\n\n',
        {
          headers: {
            "Content-Type": "text/event-stream; charset=utf-8",
          },
        },
      ),
    );

    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(mockStartDocumentAiOrchestratorRun).toHaveBeenCalledWith(
      expect.objectContaining({
        userId: "user-1",
      }),
    );
    expect(mockStartHermesRun).not.toHaveBeenCalled();
    expect(text).toContain("event: ready");
    expect(text).toContain("来自 orchestrator");
  });

  it("文档页在线请求应把 modelKey、profileId 和 sessionId 透传到 orchestrator", async () => {
    mockSafeGetJsonBody.mockResolvedValue({
      stream: true,
      scope: "document",
      messages: [{ role: "user", content: "总结当前页面" }],
      context: {
        documentId: "doc-1",
        documentBlocks: [],
      },
      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",
      },
    });
    mockStartDocumentAiOrchestratorRun.mockResolvedValue(
      new Response(
        'event: assistant_message\ndata: {"text":"来自 orchestrator"}\n\n',
        {
          headers: {
            "Content-Type": "text/event-stream; charset=utf-8",
          },
        },
      ),
    );

    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(mockStartDocumentAiOrchestratorRun).toHaveBeenCalledWith(
      expect.objectContaining({
        userId: "user-1",
        payload: expect.objectContaining({
          options: expect.objectContaining({
            ai: expect.objectContaining({
              sessionId: "doc_ai:doc-1:session-1",
              modelKey: "gpt-5.3-codex",
              profileId: "page_writer_polish",
            }),
          }),
        }),
      }),
    );
  });

  it("orchestrator 失败时应自动回退 Hermes", async () => {
    mockSafeGetJsonBody.mockResolvedValue({
      stream: true,
      scope: "document",
      messages: [{ role: "user", content: "把标题改成回退标题" }],
      context: {
        documentId: "doc-1",
      },
      options: {
        ai: {
          provider: "online",
        },
      },
    });
    mockValidateRequestBody.mockReturnValue(null);
    mockIsConvexEnabled.mockReturnValue(true);
    mockGetAuthedConvexClient.mockResolvedValue({
      auth: {
        userId: "user-1",
      },
    });
    mockStartDocumentAiOrchestratorRun.mockRejectedValue(new Error("sidecar down"));
    mockStartHermesRun.mockResolvedValue({
      runId: "run-fallback",
    });
    mockStreamHermesRunEvents.mockImplementation(async (_runId, onEvent) => {
      await onEvent({
        event: "run.completed",
        output: "Hermes fallback",
      });
    });

    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(mockStartDocumentAiOrchestratorRun).toHaveBeenCalledTimes(1);
    expect(mockStartHermesRun).toHaveBeenCalledTimes(1);
    expect(text).toContain("Hermes fallback");
  });
});
