#!/usr/bin/env node
"use strict";

const assert = require("node:assert");
const { chromium } = require("playwright");
const {
  BASE_URL,
  cleanupDocuments,
  createTempDocument,
  ensureAuthenticated,
  renameDocument,
  requestJson,
} = require("./tree-shell-smoke-helpers");

async function main() {
  const suffix = Date.now().toString(36);
  const title = `TEST-HERMES-AI-audit-${suffix}`;
  const marker = `TEST-HERMES-AI-AUDIT-${suffix}`;
  const createdIds = [];
  const browser = await chromium.launch({ headless: true });
  const context = await browser.newContext({ viewport: { width: 1280, height: 900 } });
  const page = await context.newPage();

  try {
    await ensureAuthenticated(page, context.request);
    const target = await createTempDocument(context.request);
    createdIds.push(target.documentId);
    await renameDocument(context.request, target.workspaceId, target.documentId, title);

    const readTraceId = `trace_audit_read_${suffix}`;
    const writeTraceId = `trace_audit_write_${suffix}`;
    const deniedTraceId = `trace_audit_denied_${suffix}`;
    const readToolCallId = `call_audit_read_${suffix}`;
    const writeToolCallId = `call_audit_write_${suffix}`;
    const deniedToolCallId = `call_audit_denied_${suffix}`;

    const read = await requestJson(context.request, "/api/hermes/tools/mnote/call", {
      method: "POST",
      headers: { "x-mnote-actor-id": "smoke-user" },
      data: {
        toolName: "mnote.page.get",
        workspaceId: target.workspaceId,
        documentId: target.documentId,
        actorId: "smoke-user",
        sessionId: `mnote_audit_${suffix}`,
        runId: `run_audit_read_${suffix}`,
        toolCallId: readToolCallId,
        traceId: readTraceId,
        capabilityScope: ["page.read"],
      },
    });
    assert.equal(read.audit.effect, "read", "读工具 audit effect 应为 read");

    const write = await requestJson(context.request, "/api/hermes/tools/mnote/call", {
      method: "POST",
      headers: { "x-mnote-actor-id": "smoke-user" },
      data: {
        toolName: "mnote.page.save",
        workspaceId: target.workspaceId,
        documentId: target.documentId,
        actorId: "smoke-user",
        sessionId: `mnote_audit_${suffix}`,
        runId: `run_audit_write_${suffix}`,
        toolCallId: writeToolCallId,
        traceId: writeTraceId,
        idempotencyKey: `idem_audit_write_${suffix}`,
        dryRun: false,
        capabilityScope: ["page.write"],
        args: {
          mode: "replace",
          content: [
            {
              id: `audit_block_${suffix}`,
              type: "paragraph",
              content: [{ type: "text", text: marker }],
            },
          ],
        },
      },
    });
    assert.equal(write.audit.effect, "write", "写工具 audit effect 应为 write");
    assert(write.audit.commandId, "写工具 audit 必须包含 commandId");

    const deniedResponse = await context.request.fetch(`${BASE_URL}/api/hermes/tools/mnote/call`, {
      method: "POST",
      headers: {
        "content-type": "application/json",
        "x-mnote-actor-id": "smoke-user",
        "x-mnote-workspace-id": "ws_denied",
      },
      data: JSON.stringify({
        toolName: "mnote.page.save",
        workspaceId: target.workspaceId,
        documentId: target.documentId,
        actorId: "smoke-user",
        sessionId: `mnote_audit_${suffix}`,
        runId: `run_audit_denied_${suffix}`,
        toolCallId: deniedToolCallId,
        traceId: deniedTraceId,
        idempotencyKey: `idem_audit_denied_${suffix}`,
        dryRun: false,
        capabilityScope: ["page.write"],
        args: {
          mode: "replace",
          content: [
            {
              id: `audit_denied_block_${suffix}`,
              type: "paragraph",
              content: [{ type: "text", text: marker }],
            },
          ],
        },
      }),
    });
    const deniedText = await deniedResponse.text();
    assert.equal(deniedResponse.status(), 403, "权限失败 smoke 应返回 workspace_context_conflict");
    assert(!deniedText.includes(title), "权限失败响应不应包含页面标题");
    assert(!deniedText.includes(marker), "权限失败响应不应包含正文 marker");

    const readAudit = await requestJson(
      context.request,
      `/api/hermes/tools/mnote/audit?traceId=${encodeURIComponent(readTraceId)}`,
      {
        method: "GET",
        headers: { "x-mnote-actor-id": "smoke-user" },
      },
    );
    const writeAudit = await requestJson(
      context.request,
      `/api/hermes/tools/mnote/audit?traceId=${encodeURIComponent(writeTraceId)}`,
      {
        method: "GET",
        headers: { "x-mnote-actor-id": "smoke-user" },
      },
    );
    const writePersistedAudit = await requestJson(
      context.request,
      `/api/hermes/tools/mnote/audit?traceId=${encodeURIComponent(writeTraceId)}&persistedOnly=true`,
      {
        method: "GET",
        headers: { "x-mnote-actor-id": "smoke-user" },
      },
    );
    const deniedPersistedAudit = await requestJson(
      context.request,
      `/api/hermes/tools/mnote/audit?traceId=${encodeURIComponent(deniedTraceId)}&persistedOnly=true`,
      {
        method: "GET",
        headers: { "x-mnote-actor-id": "smoke-user" },
      },
    );

    const readPhases = new Set((readAudit.events || []).map((event) => event.phase));
    const writePhases = new Set((writeAudit.events || []).map((event) => event.phase));
    const writePersistedPhases = new Set((writePersistedAudit.events || []).map((event) => event.phase));
    const deniedPersistedPhases = new Set((deniedPersistedAudit.events || []).map((event) => event.phase));
    assert(readPhases.has("started"), "读工具 audit 缺少 started");
    assert(readPhases.has("completed"), "读工具 audit 缺少 completed");
    assert(writePhases.has("started"), "写工具 audit 缺少 started");
    assert(writePhases.has("completed"), "写工具 audit 缺少 completed");
    assert(writePersistedPhases.has("started"), "持久化写工具 audit 缺少 started");
    assert(writePersistedPhases.has("completed"), "持久化写工具 audit 缺少 completed");
    assert(deniedPersistedPhases.has("failed"), "持久化权限失败 audit 缺少 failed");
    assert(
      (writeAudit.events || []).some((event) => event.audit?.commandId === write.audit.commandId),
      "写工具 audit 查询结果未串到 Rust commandId",
    );
    assert(
      (writePersistedAudit.events || []).some((event) => event.audit?.commandId === write.audit.commandId),
      "持久化写工具 audit 查询结果未串到 Rust commandId",
    );
    assert(!JSON.stringify(writeAudit).includes(marker), "audit 查询结果不应包含完整正文内容");
    assert(!JSON.stringify(writePersistedAudit).includes(marker), "持久化 audit 不应包含完整正文内容");
    assert(!JSON.stringify(deniedPersistedAudit).includes(marker), "权限失败持久化 audit 不应包含完整正文内容");
    assert(!JSON.stringify(deniedPersistedAudit).includes(title), "权限失败持久化 audit 不应包含页面标题");

    console.log(
      JSON.stringify(
        {
          ok: true,
          baseUrl: BASE_URL,
          documentId: target.documentId,
          workspaceId: target.workspaceId,
          readTraceId,
          writeTraceId,
          deniedTraceId,
          writeCommandId: write.audit.commandId,
          readPhases: Array.from(readPhases),
          writePhases: Array.from(writePhases),
          writePersistedPhases: Array.from(writePersistedPhases),
          deniedPersistedPhases: Array.from(deniedPersistedPhases),
        },
        null,
        2,
      ),
    );
  } finally {
    await cleanupDocuments(context.request, createdIds).catch(() => undefined);
    await context.close().catch(() => undefined);
    await browser.close().catch(() => undefined);
  }
}

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