"use strict";

const { chromium } = require("playwright");
const {
  BASE_URL,
  UI_TIMEOUT_MS,
  assert,
  createTempDocument,
  ensureAuthenticated,
  purgeDocument,
} = require("./tree-shell-smoke-helpers");

function readDocumentIdFromUrl(url) {
  try {
    const parsed = new URL(url);
    const match = parsed.pathname.match(/^\/documents\/([^/]+)$/);
    return match ? match[1] : null;
  } catch {
    return null;
  }
}

async function collectRuntimeIslandDiagnostics(page) {
  return page.evaluate(() => {
    const host = document.querySelector('[data-testid="mnote-leptos-tiptap-island-editor-root"]');
    const observability = document.querySelector("[data-editor-host-observability]");
    const editor = host?.querySelector(".editor-surface .ProseMirror");
    const textareaCount = host?.querySelectorAll("textarea").length ?? 0;
    const contenteditableCount = host?.querySelectorAll(".editor-surface .ProseMirror[contenteditable]").length ?? 0;
    return {
      hostKind: host?.getAttribute("data-editor-host-kind") ?? null,
      runtimeStatus: host?.getAttribute("data-runtime-editor-status") ?? null,
      activeHostKind: observability?.getAttribute("data-editor-host-active") ?? null,
      observability: observability?.getAttribute("data-editor-host-observability") ?? null,
      editorTagName: editor instanceof HTMLElement ? editor.tagName : null,
      editorIsContentEditable: editor instanceof HTMLElement ? editor.isContentEditable : false,
      editorContentEditableAttr: editor instanceof HTMLElement ? editor.getAttribute("contenteditable") : null,
      editorCount: host?.querySelectorAll(".editor-surface .ProseMirror").length ?? 0,
      textareaCount,
      contenteditableCount,
      hostHTML: host?.innerHTML?.slice(0, 2000) ?? null,
    };
  });
}

async function waitForRuntimeIsland(page) {
  const root = page.locator('[data-testid="mnote-leptos-tiptap-island-editor-root"]');
  await root.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
  try {
    await page.waitForFunction(
      () => {
        const host = document.querySelector('[data-testid="mnote-leptos-tiptap-island-editor-root"]');
        const observability = document.querySelector("[data-editor-host-observability]");
        const editor = host?.querySelector(".editor-surface .ProseMirror");
        return (
          host?.getAttribute("data-editor-host-kind") === "leptos_tiptap_island" &&
          observability?.getAttribute("data-editor-host-active") === "leptos_tiptap_island" &&
          host?.getAttribute("data-runtime-editor-status") !== "error" &&
          editor instanceof HTMLElement &&
          editor.isContentEditable === true
        );
      },
      null,
      { timeout: UI_TIMEOUT_MS },
    );
  } catch (error) {
    const diagnostics = await collectRuntimeIslandDiagnostics(page).catch(() => null);
    throw new Error(
      `${error instanceof Error ? error.message : String(error)}\n${JSON.stringify(diagnostics, null, 2)}`,
    );
  }

  const editorCount = await root.locator(".editor-surface .ProseMirror").count();
  const textareaCount = await root.locator("textarea").count();
  const contenteditableCount = await root.locator(".editor-surface .ProseMirror[contenteditable]").count();
  if (editorCount === 0 || contenteditableCount === 0 || textareaCount > 0) {
    const diagnostics = await collectRuntimeIslandDiagnostics(page).catch(() => null);
    throw new Error(
      [
        editorCount === 0 ? "island 主编辑器根节点内缺少 `.editor-surface .ProseMirror` surface" : null,
        contenteditableCount === 0 ? "island 主编辑器 surface 未暴露真实 contenteditable" : null,
        textareaCount > 0 ? "island 主编辑器根节点内不应回退为 textarea" : null,
      ]
        .filter(Boolean)
        .join("；") +
        `\n${JSON.stringify(diagnostics, null, 2)}`,
    );
  }
}

async function waitForSaved(page) {
  await page.waitForFunction(
    () =>
      document
        .querySelector('[data-editor-host-kind="leptos_tiptap_island"]')
        ?.getAttribute("data-runtime-editor-status") === "saved",
    null,
    { timeout: UI_TIMEOUT_MS },
  );
}

async function readEditorText(page) {
  return page.evaluate(() => {
    const editor = document.querySelector(
      '[data-testid="mnote-leptos-tiptap-island-editor-root"] .editor-surface .ProseMirror',
    );
    return editor?.textContent ?? "";
  });
}

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 createdDocumentId = null;
  let createdWorkspaceId = null;

  try {
    await ensureAuthenticated(page, context.request);
    const created = await createTempDocument(context.request, null);
    createdDocumentId = created.documentId;
    createdWorkspaceId = created.workspaceId;

    await page.goto(
      `${BASE_URL}/documents/${createdDocumentId}?workspaceId=${encodeURIComponent(createdWorkspaceId)}`,
      { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS },
    );
    assert(
      page.url().includes(`/documents/${createdDocumentId}`),
      `未进入新建页面：${page.url()}`,
    );

    await waitForRuntimeIsland(page);

    const editor = page
      .locator('[data-testid="mnote-leptos-tiptap-island-editor-root"] .editor-surface .ProseMirror[contenteditable="true"]')
      .first();
    const text = `task108-island-${Date.now().toString().slice(-6)}`;

    await editor.evaluate((el) => {
      if (el instanceof HTMLElement) {
        el.focus();
      }
    });
    await page.keyboard.type(text, { delay: 30 });
    await waitForSaved(page);
    assert((await readEditorText(page)).includes(text), "默认 runtime island 未写入文本");

    await page.keyboard.press("Control+z");
    await page.waitForFunction(
      (expected) => {
        const editorNode = document.querySelector(
          '[data-testid="mnote-leptos-tiptap-island-editor-root"] .editor-surface .ProseMirror[contenteditable="true"]',
        );
        return !(editorNode?.textContent ?? "").includes(expected);
      },
      text,
      { timeout: UI_TIMEOUT_MS },
    );

    await page.keyboard.press("Control+y");
    await page.waitForFunction(
      (expected) => {
        const editorNode = document.querySelector(
          '[data-testid="mnote-leptos-tiptap-island-editor-root"] .editor-surface .ProseMirror[contenteditable="true"]',
        );
        return (editorNode?.textContent ?? "").includes(expected);
      },
      text,
      { timeout: UI_TIMEOUT_MS },
    );
    await waitForSaved(page);

    await page.reload({ waitUntil: "commit", timeout: UI_TIMEOUT_MS });
    await waitForRuntimeIsland(page);
    assert((await readEditorText(page)).includes(text), "刷新后未回填 runtime island 保存内容");

    await page.goto(
      `${BASE_URL}/documents/${createdDocumentId}?workspaceId=${encodeURIComponent(createdWorkspaceId)}&editorHost=blocknote`,
      { waitUntil: "commit", timeout: UI_TIMEOUT_MS },
    );
    await page.waitForFunction(
      () =>
        document
          .querySelector("[data-editor-host-observability]")
          ?.getAttribute("data-editor-host-active") === "blocknote",
      null,
      { timeout: UI_TIMEOUT_MS },
    );
    const runtimeIslandCount = await page
      .locator('[data-testid="mnote-leptos-tiptap-island-editor-root"]')
      .count();
    assert(runtimeIslandCount === 0, "显式 blocknote 回退下不应继续挂载 island 主编辑器");

    console.log(
      JSON.stringify(
        {
          ok: true,
          baseUrl: BASE_URL,
          documentId: createdDocumentId,
          workspaceId: createdWorkspaceId,
          text,
        },
        null,
        2,
      ),
    );
  } catch (error) {
    caughtError = error;
  } finally {
    if (createdDocumentId) {
      try {
        await purgeDocument(context.request, createdDocumentId);
      } catch (cleanupError) {
        if (!caughtError) {
          caughtError = 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);
});
