import { EventEmitter } from "node:events";
import { PassThrough } from "node:stream";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { PageOptionsState } from "@/types/page-options";

const {
  mockSpawn,
  mockBuildDocumentBridgeContext,
  mockBuildDocumentCommandEnvelope,
  mockExecutePageWriteBridgeCommand,
} = vi.hoisted(() => ({
  mockSpawn: vi.fn(),
  mockBuildDocumentBridgeContext: vi.fn(),
  mockBuildDocumentCommandEnvelope: vi.fn(),
  mockExecutePageWriteBridgeCommand: vi.fn(),
}));

vi.mock("node:child_process", () => ({
  default: {
    spawn: mockSpawn,
  },
  spawn: mockSpawn,
}));

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/documents/bridge", () => ({
  buildDocumentBridgeContext: mockBuildDocumentBridgeContext,
  buildDocumentCommandEnvelope: mockBuildDocumentCommandEnvelope,
}));

vi.mock("@/lib/documents/page-write-command-adapter", () => ({
  executePageWriteBridgeCommand: mockExecutePageWriteBridgeCommand,
}));

import {
  extractPageOptionsPatchFromAiMessage,
  startMnoteCliAgentHostRun,
} from "./mnote-cli-agent-host";

function buildPageOptions(overrides: Partial<PageOptionsState> = {}): PageOptionsState {
  return {
    wideLayout: false,
    smallText: false,
    showHeadingNumbers: false,
    showToc: false,
    showStructure: false,
    protectEditing: false,
    showWordCount: true,
    collapseBacklinks: false,
    pageFont: "default",
    layoutDensity: "normal",
    hideChildPages: false,
    showBlockRefCount: false,
    embedDefaultBlockId: null,
    ...overrides,
  };
}

function createMockChild(stdoutText: string) {
  const child = new EventEmitter() as EventEmitter & {
    stdout: PassThrough;
    stderr: PassThrough;
    kill: ReturnType<typeof vi.fn>;
  };
  child.stdout = new PassThrough();
  child.stderr = new PassThrough();
  child.kill = vi.fn();
  setTimeout(() => {
    child.stdout.end(stdoutText);
    child.stderr.end("");
    child.emit("close", 0);
  }, 0);
  return child;
}

describe("startMnoteCliAgentHostRun", () => {
  beforeEach(() => {
    mockSpawn.mockReset();
    mockBuildDocumentBridgeContext.mockReset();
    mockBuildDocumentCommandEnvelope.mockReset();
    mockExecutePageWriteBridgeCommand.mockReset();
    delete process.env.DEV_USER_ID;
    delete process.env.DEV_USER_EMAIL;
    delete process.env.DEV_USER_NAME;
  });

  it("启动 mnote-cli 时应把当前 Web 用户作为默认 CLI 写入身份下发", async () => {
    mockSpawn.mockImplementation(() => createMockChild('{"ok":true}\n'));

    const response = await startMnoteCliAgentHostRun({
      request: new Request("http://127.0.0.1:3000/api/ai-agent/run"),
      userId: "a4b72c17-49e3-46d3-8456-24a0a7044d64",
      userEmail: "dev@mnote.local",
      userName: "开发用户",
      payload: {
        stream: false,
        messages: [{ role: "user", content: "写入测试页" }],
        context: {
          documentId: "doc-1",
          workspaceId: "ws_req_1778035004501_15",
        },
      },
    });

    expect(response.status).toBe(200);
    expect(mockSpawn).toHaveBeenCalledWith(
      "cargo",
      expect.arrayContaining([
        "--args-json",
        expect.stringContaining('"workspaceId":"ws_req_1778035004501_15"'),
      ]),
      expect.objectContaining({
        env: expect.objectContaining({
          DEV_USER_ID: "a4b72c17-49e3-46d3-8456-24a0a7044d64",
          DEV_USER_EMAIL: "dev@mnote.local",
          DEV_USER_NAME: "开发用户",
          MNOTE_CLI_ALLOW_CREATE_PAGE: "1",
          MNOTE_CLI_ALLOW_EDIT: "1",
        }),
      }),
    );
  });

  it("应把当前页面 workspaceId 传入 CLI argsJson，避免 agent 写入错误工作空间", async () => {
    mockSpawn.mockImplementation(() => createMockChild('{"ok":true}\n'));

    await startMnoteCliAgentHostRun({
      request: new Request("http://127.0.0.1:3000/api/ai-agent/run"),
      userId: "a4b72c17-49e3-46d3-8456-24a0a7044d64",
      userEmail: "dev@mnote.local",
      userName: "开发用户",
      payload: {
        stream: false,
        messages: [{ role: "user", content: "在当前空间写测试页" }],
        context: {
          documentId: "tree_1778036320856_1",
          workspaceId: "ws_req_1778035004501_15",
        },
      },
    });

    const args = mockSpawn.mock.calls[0]?.[1] as string[];
    const argsJson = args[args.indexOf("--args-json") + 1];
    expect(JSON.parse(argsJson)).toMatchObject({
      documentId: "tree_1778036320856_1",
      workspaceId: "ws_req_1778035004501_15",
    });
  });

  it("应从自然语言页面设置请求中提取 wired patch", () => {
    expect(
      extractPageOptionsPatchFromAiMessage({
        messages: [{ role: "user", content: "请显示目录，改成紧凑排版，并切换成宋体" }],
        currentPageOptions: buildPageOptions(),
      }),
    ).toEqual({
      showToc: true,
      layoutDensity: "compact",
      pageFont: "song",
    });
  });

  it("命中页面设置 patch 时应直接发结构化 tool_result，而不是启动 cargo", async () => {
    mockBuildDocumentBridgeContext.mockResolvedValue({
      workspaceId: "ws-1",
      requestId: "req-1",
      traceId: "trace-1",
      actor: {
        actorType: "user",
        actorId: "user-1",
        sessionId: null,
      },
      source: {
        channel: "next-route",
        client: "wolai-frontend",
      },
      deploymentId: null,
      projectId: null,
      tenantId: null,
      authToken: null,
      idempotencyKey: null,
      validateOnly: false,
      dryRun: false,
    });
    mockBuildDocumentCommandEnvelope.mockImplementation((input) => ({
      ...input,
      commandId: "cmd-1",
      idempotencyKey: null,
      actor: input.context.actor,
      source: input.context.source,
      target: input.target ?? null,
      preflightData: null,
      reason: input.reason ?? null,
      refs: input.refs ?? [],
      dryRun: input.context.dryRun,
      validateOnly: input.context.validateOnly,
    }));
    mockExecutePageWriteBridgeCommand.mockResolvedValue({
      requestId: "req-1",
      traceId: "trace-1",
      commandId: "cmd-1",
      commandName: "page.layout.updateOptions",
      revision: null,
      conflictDetectionKey: null,
    });

    const response = await startMnoteCliAgentHostRun({
      request: new Request("http://127.0.0.1:3000/api/ai-agent/run"),
      userId: "user-1",
      payload: {
        stream: true,
        messages: [{ role: "user", content: "请显示目录，并切换成紧凑排版" }],
        context: {
          documentId: "doc-1",
          workspaceId: "ws-1",
          pageOptions: buildPageOptions(),
        },
      },
    });

    const text = await response.text();

    expect(mockSpawn).not.toHaveBeenCalled();
    expect(mockExecutePageWriteBridgeCommand).toHaveBeenCalledTimes(1);
    expect(text).toContain("event: tool_call");
    expect(text).toContain('"tool":"page_options_patch"');
    expect(text).toContain('"action":"update_page_options"');
    expect(text).toContain('"showToc":true');
    expect(text).toContain('"layoutDensity":"compact"');
  });
});
