"use strict";

const { chromium } = require("playwright");
const {
  BASE_URL,
  UI_TIMEOUT_MS,
  assert,
  cleanupDocuments,
  ensureAuthenticated,
  ensurePageOptionsVisible,
  openDocument,
  openFilesystemView,
  openSectionView,
  prepareTempTreeFixture,
} = require("./tree-shell-smoke-helpers");

async function runPageTreeChecks(page, fixture) {
  await openDocument(page, fixture.workspaceId, fixture.parentId);
  await openSectionView(page);

  const shellHost = page.getByTestId("sidebar-page-tree-shell");
  await shellHost.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
  await shellHost.locator(`[data-testid="page-tree-row"][data-node-id="${fixture.parentId}"]`).waitFor({
    state: "visible",
    timeout: UI_TIMEOUT_MS,
  });
  await shellHost.locator(`[data-testid="page-tree-row"][data-node-id="${fixture.childId}"]`).waitFor({
    state: "visible",
    timeout: UI_TIMEOUT_MS,
  });

  return {
    pageTreeVisible: true,
  };
}

async function runFileTreeChecks(page, fixture) {
  await openFilesystemView(page);

  const shellHost = page.getByTestId("sidebar-file-tree-shell");
  const openStart = Date.now();
  await shellHost.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });

  const parentDocRow = shellHost.locator(`[data-testid="filetree-doc-row"][data-doc-id="${fixture.parentId}"]`).first();
  const childDocRow = shellHost.locator(`[data-testid="filetree-doc-row"][data-doc-id="${fixture.childId}"]`).first();
  const parentIndexRow = shellHost.locator(`[data-testid="filetree-index-row"][data-doc-id="${fixture.parentId}"]`).first();
  await parentDocRow.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
  await childDocRow.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
  await parentIndexRow.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
  const filetreeVisibleMs = Date.now() - openStart;

  assert(
    (await shellHost.locator('iframe[title="mnote-web tree shell"]').count()) === 0,
    "文件树主路径仍残留 iframe tree shell",
  );

  const navigateStart = Date.now();
  await childDocRow.click({ timeout: UI_TIMEOUT_MS });
  await page.waitForURL((url) => url.toString().includes(`/documents/${fixture.childId}`), {
    timeout: UI_TIMEOUT_MS,
  });
  const filetreeNavigateMs = Date.now() - navigateStart;

  return {
    filetreeVisibleMs,
    filetreeNavigateMs,
  };
}

async function runPickerChecks(page, fixture) {
  await ensurePageOptionsVisible(page);

  const openButton = page.getByRole("button", { name: "移动/嵌入到..." });
  await openButton.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });

  const openStart = Date.now();
  await openButton.click({ timeout: UI_TIMEOUT_MS });
  const dialog = page.getByRole("dialog");
  await dialog.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
  const pickerSurface = dialog.getByTestId("tree-picker-surface");
  await pickerSurface.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
  const pickerVisibleMs = Date.now() - openStart;

  assert(
    (await dialog.locator('iframe[title="mnote-web tree shell"]').count()) === 0,
    "picker 默认路径仍残留 iframe tree shell",
  );

  const rootPick = dialog.getByTestId("tree-picker-root");
  await rootPick.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
  await page.keyboard.press("Escape");
  await dialog.waitFor({ state: "hidden", timeout: UI_TIMEOUT_MS });

  await openButton.click({ timeout: UI_TIMEOUT_MS });
  await dialog.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
  const searchInput = dialog.getByPlaceholder("移动到...");
  await searchInput.fill(fixture.childTitle, { timeout: UI_TIMEOUT_MS });
  const searchStart = Date.now();
  await page.waitForTimeout(1000);
  const nextErrorOverlay = page.locator('[data-nextjs-dialog-content="true"]');
  assert(
    (await nextErrorOverlay.count()) === 0,
    "picker 搜索触发了前端运行时错误",
  );
  const searchButtons = dialog.getByRole("button");
  const emptyState = dialog.getByText("没有匹配结果");
  const hasSearchOutcome =
    (await searchButtons.count()) > 0 || (await emptyState.count()) > 0;
  assert(hasSearchOutcome, "picker 搜索后既没有结果也没有空态提示");
  const pickerSearchMs = Date.now() - searchStart;
  await page.keyboard.press("Escape");
  await dialog.waitFor({ state: "hidden", timeout: UI_TIMEOUT_MS });

  return {
    pickerVisibleMs,
    pickerSearchMs,
  };
}

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

  let caughtError = null;
  let fixture = null;

  try {
    const viewer = await ensureAuthenticated(page, context.request);
    fixture = await prepareTempTreeFixture(context.request);

    const pageTree = await runPageTreeChecks(page, fixture);
    const filetree = await runFileTreeChecks(page, fixture);
    const picker = await runPickerChecks(page, fixture);

    console.log(
      JSON.stringify(
        {
          ok: true,
          baseUrl: BASE_URL,
          viewerUserId: viewer.userId,
          workspaceId: fixture.workspaceId,
          parentId: fixture.parentId,
          childId: fixture.childId,
          pageTree,
          filetree,
          picker,
        },
        null,
        2,
      ),
    );
  } catch (error) {
    caughtError = error;
  } finally {
    if (fixture) {
      try {
        await cleanupDocuments(context.request, fixture.createdIds);
      } catch (cleanupError) {
        if (!caughtError) {
          caughtError = cleanupError;
        } else {
          console.error(
            `清理临时页面失败：${
              cleanupError instanceof Error
                ? cleanupError.stack || cleanupError.message
                : String(cleanupError)
            }`,
          );
        }
      }
    }
    await page.close().catch(() => undefined);
    await context.close().catch(() => undefined);
    await browser.close().catch(() => undefined);
  }

  if (caughtError) {
    throw caughtError;
  }
}

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