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

const assert = require("node:assert");
const fs = require("node:fs/promises");
const path = require("node:path");
const { chromium } = require("playwright");

const ROOT = path.resolve(__dirname, "..");
const BASE_URL = (process.env.MNOTE_UI_BASE_URL || process.env.MNOTE_WEB_SMOKE_BASE_URL || "http://127.0.0.1:3000").replace(/\/+$/, "");
const UI_TIMEOUT_MS = Number(process.env.MNOTE_SMOKE_UI_TIMEOUT_MS || 30_000);
const OUT_DIR = path.join(ROOT, "tmp", "task442-trash-modal-workbench-smoke");

async function quickLogin(page) {
  await page.goto(`${BASE_URL}/auth`, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
  const quickLoginButton = page.getByRole("button", { name: "测试账号快速登录" });
  if (await quickLoginButton.count()) {
    await quickLoginButton.click({ timeout: UI_TIMEOUT_MS });
    await page.waitForURL((url) => url.pathname === "/", { timeout: UI_TIMEOUT_MS });
  }
}

async function readState(page) {
  return page.evaluate(() => {
    const fileRoot = document.getElementById("sidebar-file-tree-root");
    const activeRow = document.querySelector("#sidebar-file-tree-root .tree-row[data-selected='true'], #sidebar-tree-root .tree-row[data-active='true']");
    const modal = document.querySelector('[data-testid="mnote-trash-modal"]');
    const panel = document.querySelector('[data-testid="mnote-trash-modal"] .mnote-trash-modal__panel');
    const workbench = document.querySelector('[data-testid="mnote-trash-modal"] [data-testid="mnote-trash-workbench"]');
    const modalRect = modal instanceof HTMLElement ? modal.getBoundingClientRect() : null;
    const panelRect = panel instanceof HTMLElement ? panel.getBoundingClientRect() : null;
    return {
      url: window.location.href,
      pathname: window.location.pathname,
      modalOpen: modal instanceof HTMLElement,
      workbenchVisible: workbench instanceof HTMLElement,
      panelRect: panelRect ? {
        left: panelRect.left,
        right: panelRect.right,
        top: panelRect.top,
        bottom: panelRect.bottom,
        width: panelRect.width,
        height: panelRect.height,
      } : null,
      viewportWidth: window.innerWidth,
      viewportHeight: window.innerHeight,
      modalRect: modalRect ? {
        left: modalRect.left,
        right: modalRect.right,
        top: modalRect.top,
        bottom: modalRect.bottom,
      } : null,
      fileScrollTop: fileRoot instanceof HTMLElement ? fileRoot.scrollTop : null,
      activeRowId: activeRow instanceof HTMLElement ? activeRow.getAttribute("data-row-id") || activeRow.getAttribute("data-node-id") || "" : "",
      bodyShell: document.body.getAttribute("data-mnote-shell") || "",
      modalRole: modal instanceof HTMLElement ? modal.getAttribute("role") || "" : "",
    };
  });
}

async function requestJson(request, pathName, data) {
  const response = await request.fetch(`${BASE_URL}${pathName}`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    data,
    timeout: UI_TIMEOUT_MS,
  });
  const payload = await response.json().catch(() => null);
  if (!response.ok()) {
    throw new Error(`${pathName} failed ${response.status()}: ${JSON.stringify(payload)}`);
  }
  return payload;
}

async function treeCommand(request, body) {
  const payload = await requestJson(request, "/api/tree/commands", body);
  assert(payload?.result, `tree command 缺少 result: ${JSON.stringify(payload)}`);
  return payload.result;
}

async function readWorkspaceId(page) {
  const workspaceId = await page.evaluate(() => {
    const node = document.querySelector("#sidebar-file-tree-root[data-workspace-id], #sidebar-tree-root[data-workspace-id], [data-workspace-id]");
    return node instanceof HTMLElement ? node.getAttribute("data-workspace-id") || "" : "";
  });
  assert(workspaceId && workspaceId !== "default" && !workspaceId.startsWith("local:"), `缺少真实云空间 workspaceId: ${workspaceId}`);
  return workspaceId;
}

async function main() {
  await fs.mkdir(OUT_DIR, { recursive: true });
  const browser = await chromium.launch({ headless: process.env.HEADFUL !== "1" });
  const context = await browser.newContext({ viewport: { width: 1440, height: 960 } });
  const page = await context.newPage();
  const requests = [];
  page.on("request", (request) => {
    if (request.url().includes("/trash")) requests.push({ url: request.url(), method: request.method() });
  });

  const result = { ok: false, baseUrl: BASE_URL, workspaceId: null, archivedDocumentId: null, before: null, afterOpen: null, afterRestore: null, afterClose: null, requests };

  try {
    await quickLogin(page);
    await page.goto(BASE_URL, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
    await page.locator('[data-testid="wolai-sidebar"]').waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
    result.workspaceId = await readWorkspaceId(page);
    const created = await treeCommand(context.request, {
      action: "create",
      workspaceId: result.workspaceId,
      parentId: null,
      title: `TRASH-MODAL-${Date.now().toString().slice(-6)}`,
    });
    result.archivedDocumentId = created.documentId;
    assert(result.archivedDocumentId, `创建临时页面缺少 documentId: ${JSON.stringify(created)}`);
    await treeCommand(context.request, {
      action: "archive",
      workspaceId: result.workspaceId,
      documentId: result.archivedDocumentId,
    });
    result.before = await readState(page);

    const trashEntry = page.locator('[data-testid="mnote-sidebar-trash-entry"], .wolai-sidebar-footer .wolai-footer-entry[href="/trash"]').first();
    await trashEntry.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
    await trashEntry.click({ timeout: UI_TIMEOUT_MS });

    await page.locator('[data-testid="mnote-trash-modal"]').waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
    await page.locator('[data-testid="mnote-trash-modal"] [data-testid="mnote-trash-workbench"]').waitFor({
      state: "visible",
      timeout: UI_TIMEOUT_MS,
    });
    result.afterOpen = await readState(page);
    assert.equal(result.afterOpen.url, result.before.url, "点击垃圾箱应打开弹窗，不应离开当前 URL");
    assert.equal(result.afterOpen.modalOpen, true, "应出现垃圾箱弹窗");
    assert.equal(result.afterOpen.workbenchVisible, true, "弹窗内应复用 mnote-trash-workbench");
    assert.equal(result.afterOpen.modalRole, "dialog", "垃圾箱弹窗应使用 dialog role");
    assert(result.afterOpen.panelRect, "垃圾箱弹窗应包含面板");
    assert(Math.abs((result.afterOpen.panelRect.left + result.afterOpen.panelRect.right) / 2 - result.afterOpen.viewportWidth / 2) <= 8,
      `垃圾箱面板应居中显示: ${JSON.stringify(result.afterOpen.panelRect)}`);
    assert(result.afterOpen.panelRect.top > 20 && result.afterOpen.panelRect.bottom < result.afterOpen.viewportHeight - 20,
      `垃圾箱面板应保留上下留白: ${JSON.stringify(result.afterOpen.panelRect)}`);
    const trashRow = page.locator(`[data-testid="mnote-trash-modal"] [data-trash-row="document"][data-document-id="${result.archivedDocumentId}"]`);
    await trashRow.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
    await trashRow.locator('[data-trash-action="restore"]').click({ timeout: UI_TIMEOUT_MS });
    await trashRow.waitFor({ state: "detached", timeout: UI_TIMEOUT_MS });
    result.afterRestore = await readState(page);
    assert.equal(result.afterRestore.url, result.before.url, "弹窗内恢复页面后仍应停留在原 URL");

    await page.locator('[data-testid="mnote-trash-modal-close"]').click({ timeout: UI_TIMEOUT_MS });
    await page.locator('[data-testid="mnote-trash-modal"]').waitFor({ state: "detached", timeout: UI_TIMEOUT_MS });
    result.afterClose = await readState(page);
    assert.equal(result.afterClose.url, result.before.url, "关闭垃圾箱弹窗后仍应停留在原 URL");
    assert.equal(result.afterClose.fileScrollTop, result.before.fileScrollTop, "关闭弹窗后 File Tree scrollTop 不应被无关重置");
    assert.equal(result.afterClose.activeRowId, result.before.activeRowId, "关闭弹窗后 active/selected row 不应被无关重置");

    result.ok = true;
  } finally {
    if (result.workspaceId && result.archivedDocumentId) {
      await treeCommand(context.request, {
        action: "archive",
        workspaceId: result.workspaceId,
        documentId: result.archivedDocumentId,
      }).catch(() => {});
      await treeCommand(context.request, {
        action: "purge",
        workspaceId: result.workspaceId,
        documentId: result.archivedDocumentId,
      }).catch(() => {});
    }
    await fs.writeFile(path.join(OUT_DIR, "result.json"), `${JSON.stringify(result, null, 2)}\n`, "utf8");
    await browser.close().catch(() => {});
  }

  console.log(JSON.stringify(result, null, 2));
}

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