#!/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);
const ACTOR_ID = `smoke-user-${process.pid}-${Date.now()}`;
const MANAGED_DATA_ROOT = "/mnt/Data1T/Mnote_data";

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("/")}`;
}

function managedActorDir(actorId) {
  return path.join(MANAGED_DATA_ROOT, "users", actorId, "workspaces", "my-space");
}

function writeWorkspaceManifest(root, ownerId) {
  fs.mkdirSync(path.join(root, ".mnote"), { recursive: true });
  fs.writeFileSync(path.join(root, ".mnote", "workspace.json"), JSON.stringify({
    workspaceId: `local-ws-${path.basename(root).replace(/[^a-zA-Z0-9_-]/g, "_")}`,
    ownerId,
    createdAt: new Date(0).toISOString(),
    capabilities: ["local_files", "tree_commands", "markdown_edit", "asset_upload"],
  }, null, 2), "utf8");
}

async function main() {
  const root = fs.mkdtempSync(path.join(os.tmpdir(), "mnote-main-local-folder-"));
  const otherRoot = fs.mkdtempSync(path.join(os.tmpdir(), "mnote-main 本地 #other-"));
  fs.mkdirSync(path.join(root, "docs"), { recursive: true });
  fs.writeFileSync(path.join(root, "README.md"), "# Root Page\n", "utf8");
  fs.writeFileSync(path.join(root, "docs", "child.md"), "# Child Page\n", "utf8");
  fs.writeFileSync(path.join(root, "plain.txt"), "plain asset\n", "utf8");
  fs.writeFileSync(path.join(otherRoot, "OTHER.md"), "# Other Root\n", "utf8");
  writeWorkspaceManifest(root, ACTOR_ID);
  writeWorkspaceManifest(otherRoot, ACTOR_ID);

  const executablePath = resolveChromiumExecutablePath();
  const browser = await chromium.launch({
    headless: true,
    ...(executablePath ? { executablePath } : {}),
  });
  const context = await browser.newContext({
    extraHTTPHeaders: {
      "x-mnote-actor-id": ACTOR_ID,
      "x-mnote-actor-type": "user",
    },
  });
  const page = await context.newPage();
  const dialogs = [];
  page.on("dialog", async (dialog) => {
    dialogs.push(dialog.type());
    await dialog.dismiss().catch(() => {});
  });

  try {
    await page.goto(BASE_URL, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
    await page.locator('[data-testid="mnote-create-default-local-workspace"]').waitFor({
      state: "visible",
      timeout: UI_TIMEOUT_MS,
    });
    await page.locator('[data-testid="mnote-open-local-folder-empty"]').waitFor({
      state: "visible",
      timeout: UI_TIMEOUT_MS,
    });
    await page.locator('[data-testid="mnote-create-default-local-workspace"]').click({
      timeout: UI_TIMEOUT_MS,
    });
    const managedRoot = managedActorDir(ACTOR_ID);
    await page.waitForURL((url) => {
      return url.origin === new URL(BASE_URL).origin &&
        url.pathname === "/" &&
        url.searchParams.get("sourceKind") === "local_folder" &&
        url.searchParams.get("rootUri") === fileUrl(managedRoot) &&
        url.searchParams.get("treeView") === "filetree";
    }, { timeout: UI_TIMEOUT_MS });
    assert(
      fs.existsSync(path.join(managedRoot, ".mnote", "workspace.json")),
      "创建我的空间应写入受管 workspace manifest",
    );
    assert(
      fs.existsSync(path.join(managedRoot, "pages", "我的空间.md")),
      "创建我的空间应写入默认 Markdown 首页",
    );

    await page.locator('[data-mnote-action="open-local-folder"]').click({ timeout: UI_TIMEOUT_MS });
    await page.locator('[data-testid="mnote-local-folder-dialog"]').waitFor({
      state: "visible",
      timeout: UI_TIMEOUT_MS,
    });
    assert.deepStrictEqual(dialogs, [], "打开本地文件夹不能再触发 prompt/alert/confirm 原生弹窗");

    await page.locator('[data-testid="mnote-local-folder-path-input"]').fill(root);
    await page.locator('[data-testid="mnote-local-folder-open-confirm"]').click();
    await page.waitForURL((url) => {
      return url.origin === new URL(BASE_URL).origin &&
        url.pathname === "/" &&
        url.searchParams.get("sourceKind") === "local_folder" &&
        url.searchParams.get("rootUri") === fileUrl(root) &&
        url.searchParams.get("treeView") === "filetree";
    }, { timeout: UI_TIMEOUT_MS });
    const recentLocalRoots = await page.evaluate(() => {
      const actorId = document.body.getAttribute("data-mnote-actor-id") || "";
      const raw = window.localStorage.getItem(`mnote.localFolder.recentRoots:${encodeURIComponent(actorId)}`) || "[]";
      return JSON.parse(raw);
    });
    assert.equal(recentLocalRoots[0], fileUrl(root), "打开本地文件夹后应记录最近 rootUri");

    const bodyText = await page.locator("body").innerText({ timeout: UI_TIMEOUT_MS });
    assert(!bodyText.includes("legacy_next_compat_disabled"), "本地文件夹主入口不能落入 legacy Next fallback");
    const readmeRow = page.locator('.tree-row[data-row-id="local:markdown:README.md"]');
    const docsRow = page.locator('.tree-row[data-row-id="local:folder:docs"]');
    await readmeRow.waitFor({
      state: "visible",
      timeout: UI_TIMEOUT_MS,
    });
    await docsRow.waitFor({
      state: "visible",
      timeout: UI_TIMEOUT_MS,
    });

    await readmeRow.locator(":scope > .tree-link").click();
    await readmeRow.waitFor({
      state: "visible",
      timeout: UI_TIMEOUT_MS,
    });
    await docsRow.locator(":scope > .tree-link").click({ modifiers: ["Shift"] });
    const selectedRowIds = await page.locator('#sidebar-file-tree-root .tree-row[data-selected="true"]')
      .evaluateAll((rows) => rows.map((row) => row.getAttribute("data-row-id")));
    assert(
      selectedRowIds.includes("local:markdown:README.md") &&
        selectedRowIds.includes("local:folder:docs") &&
        selectedRowIds.length >= 2,
      `Shift+Click 应形成文件树范围多选，实际选中: ${selectedRowIds.join(", ")}`,
    );

    await page.locator('[data-testid="mnote-workspace-source-trigger"]').click({ timeout: UI_TIMEOUT_MS });
    await page.locator('[data-testid="mnote-workspace-source-menu"]').waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
    await page.locator(`[data-testid="mnote-recent-local-root"][data-root-uri="${fileUrl(root)}"]`).waitFor({
      state: "visible",
      timeout: UI_TIMEOUT_MS,
    });
    await page.locator('[data-testid="mnote-open-other-local-folder"]').click({ timeout: UI_TIMEOUT_MS });
    await page.locator('[data-testid="mnote-local-folder-dialog"]').waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
    await page.locator('[data-testid="mnote-local-folder-path-input"]').fill(otherRoot);
    await page.locator('[data-testid="mnote-local-folder-open-confirm"]').click({ timeout: UI_TIMEOUT_MS });
    await page.waitForURL((url) => {
      return url.pathname === "/" &&
        url.searchParams.get("sourceKind") === "local_folder" &&
        url.searchParams.get("rootUri") === fileUrl(otherRoot) &&
        url.searchParams.get("treeView") === "filetree";
    }, { timeout: UI_TIMEOUT_MS });
    const updatedRecentLocalRoots = await page.evaluate(() => {
      const actorId = document.body.getAttribute("data-mnote-actor-id") || "";
      const raw = window.localStorage.getItem(`mnote.localFolder.recentRoots:${encodeURIComponent(actorId)}`) || "[]";
      return JSON.parse(raw);
    });
    assert.equal(updatedRecentLocalRoots[0], fileUrl(otherRoot), "切到其他本地文件夹后应把它放到最近目录首位");
    assert.equal(updatedRecentLocalRoots[1], fileUrl(root), "之前打开过的本地目录应保留在最近目录列表中");
    const legacyRecentLocalRoots = await page.evaluate(() => {
      return window.localStorage.getItem("mnote.localFolder.recentRoots");
    });
    assert.equal(legacyRecentLocalRoots, null, "最近本地目录不能写入跨用户共享的 legacy localStorage key");
    await page.locator('.tree-row[data-row-id="local:markdown:OTHER.md"]').waitFor({
      state: "visible",
      timeout: UI_TIMEOUT_MS,
    });

    await page.locator('[data-testid="mnote-workspace-source-trigger"]').click({ timeout: UI_TIMEOUT_MS });
    await page.locator('[data-testid="mnote-workspace-source-menu"]').waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
    await page.locator('[data-testid="mnote-open-other-local-folder"]').click({ timeout: UI_TIMEOUT_MS });
    await page.locator('[data-testid="mnote-local-folder-dialog"]').waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
    const recentPathInputValue = await page.locator('[data-testid="mnote-local-folder-path-input"]').inputValue({
      timeout: UI_TIMEOUT_MS,
    });
    assert.equal(recentPathInputValue, otherRoot, "最近目录回填输入框时应解码 file:// rootUri，避免二次编码");
    await page.getByRole("button", { name: "取消" }).click({ timeout: UI_TIMEOUT_MS });

    await page.locator('[data-testid="mnote-workspace-source-trigger"]').click({ timeout: UI_TIMEOUT_MS });
    await page.locator('[data-testid="mnote-workspace-source-menu"]').waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
    await page.locator('[data-testid="mnote-switch-cloud-workspace"]').click({ timeout: UI_TIMEOUT_MS });
    await page.waitForURL((url) => {
      return url.pathname === "/" &&
        url.searchParams.get("mnoteHome") === "1" &&
        !url.searchParams.has("sourceKind") &&
        !url.searchParams.has("rootUri");
    }, { timeout: UI_TIMEOUT_MS });

    await page.goto(BASE_URL, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
    await page.waitForURL((url) => {
      return url.pathname === "/" &&
        (!url.searchParams.has("rootUri") || url.searchParams.get("rootUri") !== fileUrl(otherRoot));
    }, { timeout: UI_TIMEOUT_MS });

    console.log("task164 desktop hot local folder main entry smoke passed");
  } finally {
    await browser.close();
    fs.rmSync(root, { recursive: true, force: true });
    fs.rmSync(otherRoot, { recursive: true, force: true });
    fs.rmSync(path.join(MANAGED_DATA_ROOT, "users", ACTOR_ID), { recursive: true, force: true });
  }
}

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