"use strict";

const { chromium } = require("playwright");

const BASE_URL = process.env.MNOTE_UI_BASE_URL || "http://127.0.0.1:3000";
const REQUEST_TIMEOUT_MS = 120_000;
const UI_TIMEOUT_MS = 30_000;
const TEST_LOGIN_BUTTON_NAME = "测试账号快速登录";

async function requestJsonWithCookieHeader(path, init = {}, cookieHeader = "") {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
  try {
    const response = await fetch(`${BASE_URL}${path}`, {
      ...init,
      headers: {
        ...(init.data !== undefined ? { "content-type": "application/json" } : {}),
        ...(init.headers || {}),
        ...(cookieHeader ? { cookie: cookieHeader } : {}),
      },
      body: init.data !== undefined ? JSON.stringify(init.data) : init.body,
      signal: controller.signal,
    });

    const text = await response.text();
    let payload = null;
    try {
      payload = text ? JSON.parse(text) : null;
    } catch {
      payload = text;
    }

    if (!response.ok) {
      throw new Error(
        `${path} 请求失败: ${response.status} ${response.statusText} ${typeof payload === "string" ? payload : JSON.stringify(payload)}`,
      );
    }

    const contentType = response.headers.get("content-type") || "";
    if (!contentType.includes("application/json")) {
      throw new Error(`${path} 返回了非 JSON 内容：${String(text).slice(0, 200)}`);
    }

    return payload;
  } finally {
    clearTimeout(timer);
  }
}

function assert(condition, message) {
  if (!condition) {
    throw new Error(message);
  }
}

async function requestJson(requestContext, path, init = {}) {
  const response = await requestContext.fetch(`${BASE_URL}${path}`, {
    ...init,
    headers:
      init.data !== undefined
        ? {
            "content-type": "application/json",
            ...(init.headers || {}),
          }
        : {
            ...(init.headers || {}),
          },
    timeout: REQUEST_TIMEOUT_MS,
  });

  const text = await response.text();
  let payload = null;
  try {
    payload = text ? JSON.parse(text) : null;
  } catch {
    payload = text;
  }

  if (!response.ok()) {
    throw new Error(
      `${path} 请求失败: ${response.status()} ${response.statusText()} ${typeof payload === "string" ? payload : JSON.stringify(payload)}`,
    );
  }

  const contentType = response.headers()["content-type"] || "";
  if (!contentType.includes("application/json")) {
    throw new Error(`${path} 返回了非 JSON 内容：${String(text).slice(0, 200)}`);
  }

  return payload;
}

async function ensureAuthenticated(page, requestContext) {
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
  const tryWhoAmI = async () => {
    try {
      return await requestJson(requestContext, "/api/auth/whoami", { method: "GET" });
    } catch {
      return null;
    }
  };

  await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
  let viewer = await tryWhoAmI();
  if (viewer) return viewer;

  const quickLoginButton = page.getByRole("button", { name: TEST_LOGIN_BUTTON_NAME });
  await quickLoginButton.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
  await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });

  for (let i = 0; i < 20; i += 1) {
    await sleep(500);
    viewer = await tryWhoAmI();
    if (viewer) return viewer;
  }

  throw new Error("测试账号快速登录后仍无法获取 whoami");
}

async function createTempDocument(requestContext) {
  const payload = await requestJson(requestContext, "/api/documents/create", {
    method: "POST",
    data: { parentId: null },
  });
  assert(payload && typeof payload.id === "string", "创建临时页面失败：缺少 id");
  assert(payload && typeof payload.workspace_id === "string", "创建临时页面失败：缺少 workspace_id");
  return { documentId: payload.id, workspaceId: payload.workspace_id };
}

async function renameDocument(requestContext, documentId, workspaceId, title) {
  return await requestJson(requestContext, "/api/documents/title", {
    method: "POST",
    data: { documentId, workspaceId, title },
  });
}

async function saveDocument(requestContext, documentId, workspaceId, content) {
  return await requestJson(requestContext, "/api/documents/save", {
    method: "POST",
    data: { documentId, workspaceId, content },
  });
}

async function purgeTempDocument(requestContext, documentId) {
  return await requestJson(requestContext, "/api/documents/purge", {
    method: "POST",
    data: { documentId },
  });
}

async function runAiAgentDocsSmoke(requestContext, uniqueTitle, needleText, cookieHeader) {
  const payload = await requestJsonWithCookieHeader("/api/ai-agent/run", {
    method: "POST",
    data: {
      stream: false,
      maxSteps: 4,
      scope: "document",
      messages: [
        {
          role: "user",
          content: `<docs_search>{"query":"${uniqueTitle}","limit":5}</docs_search>`,
        },
      ],
      toolChoice: {
        mode: "manual",
        toolSets: ["toolset.docs_read"],
        tools: ["docs_search", "docs_read"],
      },
      context: {
        documentId: "smoke-doc-context",
        documentBlocks: [],
      },
      options: {
        ai: {
          provider: "codex",
          sessionId: "",
        },
      },
    },
  }, cookieHeader);

  assert(Array.isArray(payload.events), "AI Agent 返回缺少 events");
  const toolResults = payload.events.filter((event) => event && event.type === "tool_result");
  assert(toolResults.length >= 1, "AI Agent 未返回任何 tool_result");

  const searchResultEvent = toolResults.find((event) => event.data && event.data.tool === "docs_search" && event.data.ok === true);
  assert(searchResultEvent, "docs_search 未成功执行");
  const searchResults = searchResultEvent.data.result && Array.isArray(searchResultEvent.data.result.results)
    ? searchResultEvent.data.result.results
    : [];
  assert(searchResults.length >= 1, "docs_search 没有返回结果");
  const top = searchResults[0];
  assert(typeof top.id === "string" && top.id, "docs_search 首条结果缺少 documentId");

  const readPayload = await requestJsonWithCookieHeader("/api/ai-agent/run", {
    method: "POST",
    data: {
      stream: false,
      maxSteps: 4,
      scope: "document",
      messages: [
        {
          role: "user",
          content: `<docs_read>{"documentId":"${top.id}","maxChars":2500,"includeContent":false}</docs_read>`,
        },
      ],
      toolChoice: {
        mode: "manual",
        toolSets: ["toolset.docs_read"],
        tools: ["docs_read"],
      },
      context: {
        documentId: "smoke-doc-context",
        documentBlocks: [],
      },
      options: {
        ai: {
          provider: "codex",
          sessionId: "",
        },
      },
    },
  }, cookieHeader);

  assert(Array.isArray(readPayload.events), "docs_read 返回缺少 events");
  const readEvent = readPayload.events.find((event) => event && event.type === "tool_result" && event.data && event.data.tool === "docs_read" && event.data.ok === true);
  assert(readEvent, "docs_read 未成功执行");
  const rawText = String(readEvent.data.result?.rawText ?? "");
  assert(rawText.includes(needleText), `docs_read 返回未命中预期正文片段：${needleText}`);

  return {
    searchDocumentId: top.id,
    searchResultsCount: searchResults.length,
    readRawTextLength: Number(readEvent.data.result?.rawTextLength ?? 0),
  };
}

async function main() {
  const browser = await chromium.launch({ headless: true });
  const context = await browser.newContext({ viewport: { width: 1440, height: 960 } });
  const page = await context.newPage();
  let tempDocument = null;
  let caughtError = null;

  try {
    await ensureAuthenticated(page, context.request);
    tempDocument = await createTempDocument(context.request);
    const uniqueSuffix = `${Date.now()}`;
    const uniqueTitle = `task052-ai-runtime-${uniqueSuffix}`;
    const needleText = `task052 Rust docs runtime smoke ${uniqueSuffix}`;

    await renameDocument(context.request, tempDocument.documentId, tempDocument.workspaceId, uniqueTitle);
    await saveDocument(context.request, tempDocument.documentId, tempDocument.workspaceId, [
      {
        id: `task052_block_${uniqueSuffix}`,
        type: "paragraph",
        props: {},
        content: [{ type: "text", text: needleText }],
        children: [],
      },
    ]);

    const cookies = await context.cookies(BASE_URL);
    const cookieHeader = cookies.map((item) => `${item.name}=${item.value}`).join("; ");
    const runtime = await runAiAgentDocsSmoke(context.request, uniqueTitle, needleText, cookieHeader);

    console.log(JSON.stringify({
      ok: true,
      workspaceId: tempDocument.workspaceId,
      documentId: tempDocument.documentId,
      title: uniqueTitle,
      needleText,
      ...runtime,
    }, null, 2));
  } catch (error) {
    caughtError = error;
  } finally {
    if (tempDocument?.documentId) {
      try {
        await purgeTempDocument(context.request, tempDocument.documentId);
      } catch (cleanupError) {
        if (!caughtError) {
          caughtError = cleanupError;
        } else {
          console.error(`清理临时页面失败：${cleanupError instanceof Error ? cleanupError.stack || cleanupError.message : String(cleanupError)}`);
        }
      }
    }
    await page.close().catch(() => undefined);
    await context.close().catch(() => undefined);
    await browser.close().catch(() => undefined);
  }

  if (caughtError) {
    throw caughtError;
  }
}

main().catch((error) => {
  console.error(error instanceof Error ? error.stack || error.message : String(error));
  process.exit(1);
});
