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

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

const BASE_URL = (process.env.MNOTE_UI_BASE_URL || "http://127.0.0.1:3000").replace(/\/+$/, "");
const UI_TIMEOUT_MS = Number(process.env.MNOTE_SMOKE_UI_TIMEOUT_MS || 30_000);
const OUTPUT_DIR = path.resolve(__dirname, "..", "test-results");
const SCREENSHOT_PATH = path.join(OUTPUT_DIR, "task119-rust-web-wolai-visual-regression-root.png");

function assertBox(box, label) {
  assert(box, `${label} 缺少可见布局盒`);
  assert(box.width > 0 && box.height > 0, `${label} 尺寸异常: ${JSON.stringify(box)}`);
}

async function boundingBox(locator, label) {
  await locator.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
  const box = await locator.boundingBox();
  assertBox(box, label);
  return box;
}

function boxesIntersect(a, b) {
  return !(
    a.x + a.width <= b.x ||
    b.x + b.width <= a.x ||
    a.y + a.height <= b.y ||
    b.y + b.height <= a.y
  );
}

async function main() {
  fs.mkdirSync(OUTPUT_DIR, { recursive: true });

  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage({ viewport: { width: 1440, height: 960 } });

  try {
    const response = await page.goto(`${BASE_URL}/`, {
      waitUntil: "domcontentloaded",
      timeout: UI_TIMEOUT_MS,
    });
    assert(response, "根页没有返回响应");
    assert.equal(response.status(), 200, `根页状态码异常: ${response.status()}`);
    assert.equal(response.headers()["x-mnote-web-owner"], "mnote-web", "根页必须由 mnote-web 拥有");

    await page.waitForSelector('[data-mnote-shell="workspace"]', { timeout: UI_TIMEOUT_MS });
    const bodyText = await page.locator("body").innerText({ timeout: UI_TIMEOUT_MS });
    assert(!bodyText.includes("欢迎使用 MNOTE 知识管理平台"), "根页仍停留在极简欢迎页");

    const sidebar = page.getByTestId("wolai-sidebar");
    const topbar = page.getByTestId("wolai-topbar");
    const floatingAi = page.getByTestId("wolai-floating-ai");
    const floatingHelp = page.getByTestId("wolai-floating-help");
    const sidebarTreeTabs = page.getByTestId("wolai-sidebar-tree-tabs");
    const pageTreePanel = page.locator('[data-mnote-sidebar-tree-panel="page"]').first();
    const fileTreePanel = page.locator('[data-mnote-sidebar-tree-panel="filetree"]').first();
    const pageTreeTab = page.locator('[data-mnote-sidebar-tree-tab="page"]').first();
    const fileTreeTab = page.locator('[data-mnote-sidebar-tree-tab="filetree"]').first();
    const content = page.locator(".mnote-content").first();
    const staticHomeLinks = page.locator(".mnote-home-links");
    const documentShell = page.locator(".document-shell").first();
    const emptyState = page.locator('[data-testid="mnote-workspace-empty-state"]').first();

    assert.equal(await staticHomeLinks.count(), 0, "根页仍使用 .mnote-home-links 静态设计列表作为主内容");
    const hasDocumentShell = (await documentShell.count()) > 0;
    const hasEmptyState = (await emptyState.count()) > 0;
    assert(hasDocumentShell || hasEmptyState, "根页必须渲染真实 active page shell 或明确空 workspace state");

    const sidebarBox = await boundingBox(sidebar, "左侧栏");
    const topbarBox = await boundingBox(topbar, "顶栏");
    await boundingBox(sidebarTreeTabs, "页面树/文件树切换栏");
    const contentBox = await boundingBox(content, "主内容区");
    const primaryContentBox = hasDocumentShell
      ? await boundingBox(documentShell, "active page shell")
      : await boundingBox(emptyState, "空 workspace state");
    const floatingAiBox = await boundingBox(floatingAi, "AI 浮动按钮");
    const floatingHelpBox = await boundingBox(floatingHelp, "帮助浮动按钮");

    assert(sidebarBox.width >= 240 && sidebarBox.width <= 340, `左栏宽度应稳定在 Wolai 范围内: ${sidebarBox.width}`);
    assert(sidebarBox.x >= -1 && sidebarBox.x <= 1, `左栏应贴齐视口左侧: ${sidebarBox.x}`);
    assert(topbarBox.x >= sidebarBox.width - 2, "顶栏不应覆盖左侧栏");
    assert(contentBox.x >= sidebarBox.width - 2, "主内容区不应被侧栏覆盖");
    assert(topbarBox.y >= -1 && topbarBox.height >= 36 && topbarBox.height <= 72, `顶栏高度异常: ${topbarBox.height}`);
    assert(contentBox.y >= topbarBox.y + topbarBox.height - 2, "主内容区不应被顶栏遮挡");
    assert(floatingAiBox.x > sidebarBox.width, "AI 浮动按钮不应落入左侧栏");
    assert(floatingHelpBox.x > sidebarBox.width, "帮助浮动按钮不应落入左侧栏");
    assert(!boxesIntersect(floatingAiBox, primaryContentBox), "AI 浮动按钮不应遮挡主内容");
    assert(!boxesIntersect(floatingHelpBox, primaryContentBox), "帮助浮动按钮不应遮挡主内容");
    assert(floatingAiBox.y < floatingHelpBox.y, "AI 按钮应位于帮助按钮上方，匹配当前 Wolai 基线");
    assert(Math.abs((floatingHelpBox.y - (floatingAiBox.y + floatingAiBox.height)) - 16) <= 2, "AI/帮助按钮间距应接近 Wolai 16px");
    assert(Math.abs((960 - (floatingHelpBox.y + floatingHelpBox.height)) - 44) <= 4, "帮助按钮底部留白应接近 Wolai 44px");

    assert.equal(await page.locator('[data-mnote-sidebar-tree-tab="filetree"]').count(), 1, "Explorer 只能作为一个切换 tab 出现");
    assert.equal(await page.locator('[data-testid="wolai-sidebar-file-tree-section"]').count(), 0, "文件树不应再渲染成第二个独立 Explorer section");
    assert.equal(await pageTreeTab.getAttribute("aria-selected"), "true", "默认应选中我的页面 tab");
    assert.equal(await fileTreeTab.getAttribute("aria-selected"), "false", "默认不应选中 Explorer tab");
    assert.equal(await pageTreePanel.evaluate((node) => node.hidden), false, "默认应显示页面树 panel");
    assert.equal(await fileTreePanel.evaluate((node) => node.hidden), true, "默认应隐藏文件树 panel");

    await fileTreeTab.click({ timeout: UI_TIMEOUT_MS });
    await page.waitForFunction(() => {
      const filePanel = document.querySelector('[data-mnote-sidebar-tree-panel="filetree"]');
      const pagePanel = document.querySelector('[data-mnote-sidebar-tree-panel="page"]');
      return filePanel instanceof HTMLElement && pagePanel instanceof HTMLElement && !filePanel.hidden && pagePanel.hidden;
    }, null, { timeout: UI_TIMEOUT_MS });
    await boundingBox(fileTreePanel, "Explorer 文件树 panel");
    assert.equal(await fileTreeTab.getAttribute("aria-selected"), "true", "点击后应选中 Explorer tab");

    await pageTreeTab.click({ timeout: UI_TIMEOUT_MS });
    await page.waitForFunction(() => {
      const filePanel = document.querySelector('[data-mnote-sidebar-tree-panel="filetree"]');
      const pagePanel = document.querySelector('[data-mnote-sidebar-tree-panel="page"]');
      return filePanel instanceof HTMLElement && pagePanel instanceof HTMLElement && filePanel.hidden && !pagePanel.hidden;
    }, null, { timeout: UI_TIMEOUT_MS });

    await page.screenshot({ path: SCREENSHOT_PATH, fullPage: true });

    console.log(
      JSON.stringify(
        {
          ok: true,
          baseUrl: BASE_URL,
          screenshot: SCREENSHOT_PATH,
          sidebarWidth: Math.round(sidebarBox.width),
          topbarHeight: Math.round(topbarBox.height),
        },
        null,
        2,
      ),
    );
  } finally {
    await page.close().catch(() => undefined);
    await browser.close().catch(() => undefined);
  }
}

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