#!/usr/bin/env node

const assert = require("assert");
const fs = require("fs");
const os = require("os");
const path = require("path");
const { chromium } = require("playwright");

const BASE_URL = process.env.MNOTE_WEB_SMOKE_BASE_URL || "http://127.0.0.1:3000";
const UI_TIMEOUT_MS = Number(process.env.UI_TIMEOUT_MS || 10_000);

function resolveChromiumExecutablePath() {
  const explicit = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH || "";
  if (explicit && fs.existsSync(explicit)) return explicit;
  return [
    "/usr/bin/google-chrome-stable",
    "/usr/bin/google-chrome",
    "/snap/bin/chromium",
    "/usr/bin/chromium",
    "/usr/bin/chromium-browser",
  ].find((candidate) => fs.existsSync(candidate)) || "";
}

function fileUrl(filePath) {
  return `file://${filePath.split(path.sep).map((part, index) => (
    index === 0 ? "" : encodeURIComponent(part)
  )).join("/")}`;
}

async function waitForText(page, selector, expected) {
  await page.waitForFunction(
    ({ selector: targetSelector, expectedText }) => {
      const node = document.querySelector(targetSelector);
      return Boolean(node && node.textContent && node.textContent.includes(expectedText));
    },
    { selector, expectedText: expected },
    { timeout: UI_TIMEOUT_MS },
  );
  return page.locator(selector).innerText({ timeout: UI_TIMEOUT_MS });
}

async function main() {
  const root = fs.mkdtempSync(path.join(os.tmpdir(), "mnote-admin-access-policy-"));
  fs.mkdirSync(path.join(root, ".mnote"), { recursive: true });
  fs.writeFileSync(path.join(root, "README.md"), "# Admin Smoke\n", "utf8");

  const executablePath = resolveChromiumExecutablePath();
  const browser = await chromium.launch({
    headless: true,
    ...(executablePath ? { executablePath } : {}),
  });

  const adminContext = await browser.newContext({
    extraHTTPHeaders: {
      "x-mnote-actor-id": "admin_smoke",
      "x-mnote-actor-type": "admin",
    },
  });
  const adminPage = await adminContext.newPage();
  const readerContext = await browser.newContext({
    extraHTTPHeaders: {
      "x-mnote-actor-id": "reader_smoke",
      "x-mnote-actor-type": "user",
    },
  });
  const readerPage = await readerContext.newPage();

  const grantId = `grant_${Date.now()}`;
  const rootUri = fileUrl(root);

  try {
    await adminPage.goto(`${BASE_URL}/admin/access-policy`, {
      waitUntil: "domcontentloaded",
      timeout: UI_TIMEOUT_MS,
    });
    await adminPage.locator('[data-testid="mnote-admin-access-policy-page"]').waitFor({
      state: "visible",
      timeout: UI_TIMEOUT_MS,
    });
    assert(
      await adminPage.locator('[data-testid="mnote-admin-policy-path"]').innerText(),
      "管理页应显示策略路径",
    );

    await adminPage.locator('[data-testid="mnote-admin-root-uri"]').fill(rootUri);
    await adminPage.locator('[data-testid="mnote-admin-validate-root-submit"]').click({
      timeout: UI_TIMEOUT_MS,
    });
    const validateResult = await waitForText(
      adminPage,
      '[data-testid="mnote-admin-validate-result"]',
      root,
    );
    assert(validateResult.includes(root), "验证目录结果应包含 canonical root");

    await adminPage.locator('[data-testid="mnote-admin-grant-id"]').fill(grantId);
    await adminPage.locator('[data-testid="mnote-admin-grant-user-id"]').fill("reader_smoke");
    await adminPage.locator('[data-testid="mnote-admin-grant-root-uri"]').fill(rootUri);
    await adminPage.locator('[data-testid="mnote-admin-grant-permission"]').selectOption("read");
    await adminPage.locator('[data-testid="mnote-admin-grant-recursive"]').check();
    await adminPage.locator('[data-testid="mnote-admin-grant-capabilities"]').fill("ai,share");
    await adminPage.locator('[data-testid="mnote-admin-create-grant-submit"]').click({
      timeout: UI_TIMEOUT_MS,
    });
    const createResult = await waitForText(
      adminPage,
      '[data-testid="mnote-admin-create-result"]',
      grantId,
    );
    assert(createResult.includes(grantId), "创建结果应返回 grantId");

    await readerPage.goto(BASE_URL, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
    const openResponse = await readerPage.evaluate(async ({ rootUriValue }) => {
      const url = new URL("/api/local-folder/files/open", window.location.origin);
      url.searchParams.set("rootUri", rootUriValue);
      url.searchParams.set("path", "README.md");
      const response = await fetch(url.toString(), { credentials: "include" });
      return { status: response.status, text: await response.text() };
    }, { rootUriValue: rootUri });
    assert.equal(openResponse.status, 200, "read grant 应可打开本地文件");
    assert(openResponse.text.includes("Admin Smoke"), "打开的文件内容应正确");

    const writeResponse = await readerPage.evaluate(async ({ rootUriValue }) => {
      const response = await fetch("/api/page-body/write", {
        method: "POST",
        headers: { "content-type": "application/json" },
        credentials: "include",
        body: JSON.stringify({
          documentId: "local-md:README.md",
          workspaceId: "local-ws-admin-smoke",
          sourceKind: "local_folder",
          rootUri: rootUriValue,
          contentFormat: "editorBlocks",
          content: [],
        }),
      });
      return { status: response.status, text: await response.text() };
    }, { rootUriValue: rootUri });
    assert.equal(writeResponse.status, 403, "read grant 不应允许写入");

    await adminPage.locator('[data-testid="mnote-admin-delete-grant-id"]').fill(grantId);
    await adminPage.locator('[data-testid="mnote-admin-delete-grant-submit"]').click({
      timeout: UI_TIMEOUT_MS,
    });
    const deleteResult = await waitForText(
      adminPage,
      '[data-testid="mnote-admin-delete-result"]',
      grantId,
    );
    assert(deleteResult.includes(grantId), "删除结果应返回 grantId");

    await adminPage.waitForFunction(
      ({ selector, removedGrantId }) => {
        const node = document.querySelector(selector);
        return Boolean(node && node.textContent && !node.textContent.includes(removedGrantId));
      },
      { selector: '[data-testid="mnote-admin-policy-json"]', removedGrantId: grantId },
      { timeout: UI_TIMEOUT_MS },
    );
    const policyJson = await adminPage.locator('[data-testid="mnote-admin-policy-json"]').innerText();
    assert(!policyJson.includes(grantId), "删除后策略面板不应再包含已删授权");
  } finally {
    await adminContext.close().catch(() => {});
    await readerContext.close().catch(() => {});
    await browser.close().catch(() => {});
    fs.rmSync(root, { recursive: true, force: true });
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
