"use client";

import dynamic from "next/dynamic";
import {
  useCallback,
  useEffect,
  useMemo,
  useReducer,
  useRef,
  useState,
  type ChangeEvent,
  type KeyboardEvent as ReactKeyboardEvent,
} from "react";
import type { BooleanPageOptionKey, DocumentStats, PageOptionsState, PageFont, PageLayoutDensity } from "@/types/page-options";
import { useEditorBridgeStore } from "@/store/editor-bridge";
import { usePageLayoutStore } from "@/store/page-layout";
import { useCurrentDocumentStore } from "@/store/current-document";
import type { Json } from "@/types/supabase";
import type { DocumentSnapshot } from "@/types/document";
import { useDebouncedCallback } from "@/hooks/use-debounced-callback";
import { ImagePickerProvider } from "@/components/media/image-picker-context";
import { useRouter } from "next/navigation";
import { DocumentAiAgentPanel } from "./DocumentAiAgentPanel";
import { CONTENT_LOADING_DELAY_MS } from "@/lib/constants";
import { useCommentsUiStore } from "@/store/comments-ui";
import { useAppPreferencesStore } from "@/store/app-preferences";
import { useMoveEmbedPickerStore } from "@/store/move-embed-picker";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { DocumentToc } from "@/components/editor/document-toc";
import { DocumentReadView } from "@/components/editor/document-read-view";
import { emitDocumentsChanged } from "@/lib/events";
import { extractPageBlocks } from "@/lib/documents/page-subtree";
import {
  deleteDocumentCommand,
  embedDocumentCommand,
  moveDocumentCommand,
} from "@/lib/documents/tree-command-client";
import {
  executePageHeadCommand,
  executePageLayoutCommand,
  type PageBodyPersistedMeta,
} from "@/lib/documents/page-command-client";
import { EditorHost } from "@/components/editor/editor-host";
import {
  DEFAULT_EDITOR_HOST_KIND,
  isLeptosTiptapHostKind,
  type EditorHostKind,
} from "@/components/editor/editor-host-config";
import type {
  EditorHostEvent,
  EditorHostFallbackReason,
} from "@/components/editor/editor-host-types";
import type { PageAggregateProjection } from "@/lib/documents/page-aggregate";
import { usePageHeadTitle } from "@/components/editor/use-page-head-title";
import {
  createPageAggregateClientState,
  pageAggregateClientStateReducer,
  selectPageAggregateClientAiSnapshot,
  selectPageAggregateClientPageSubtree,
} from "@/components/editor/page-aggregate-client-state";

const BlockNoteEditor = dynamic(
  () => import("@/components/editor/blocknote-editor").then((mod) => mod.BlockNoteEditor),
  {
    ssr: false,
    loading: () => (
      <div className="flex h-64 items-center justify-center text-sm text-gray-400">编辑器加载中...</div>
    ),
  },
);

const PageOptionsSidebar = dynamic(
  () => import("@/components/editor/page-options-sidebar").then((mod) => mod.PageOptionsSidebar),
  { ssr: false },
);

const PageBacklinksPanel = dynamic(
  () => import("@/components/editor/page-backlinks-panel").then((mod) => mod.PageBacklinksPanel),
  {
    ssr: false,
    loading: () => null,
  },
);

const DocumentHistoryDrawer = dynamic(
  () => import("@/components/editor/document-history-drawer").then((mod) => mod.DocumentHistoryDrawer),
  {
    ssr: false,
    loading: () => null,
  },
);

const MoveEmbedPickerHost = dynamic(
  () => import("@/components/documents/move-embed-picker-host").then((mod) => mod.MoveEmbedPickerHost),
  {
    ssr: false,
    loading: () => null,
  },
);

const DocumentCommentsDrawer = dynamic(
  () => import("@/components/editor/document-comments-drawer").then((mod) => mod.DocumentCommentsDrawer),
  {
    ssr: false,
    loading: () => null,
  },
);

export interface DocumentContentProps {
  page: PageAggregateProjection;
  openTableId?: string | null;
  editorHostKind?: EditorHostKind;
}

const defaultStats: DocumentStats = { wordCount: 0, characterCount: 0, blockCount: 0, todoTotal: 0, todoDone: 0 };
const EDITOR_UNMOUNT_GRACE_MS = 1000;
const FALLBACK_TRIGGER_HISTORY_LIMIT = 20;

export async function persistPageTitleAndNotifyDocumentsChanged(input: {
  documentId: string;
  workspaceId: string;
  title: string;
  persistTitleCommand: (payload: { documentId: string; workspaceId: string; title: string }) => Promise<unknown>;
  notifyDocumentsChanged: (documentId?: string) => void;
}): Promise<string> {
  const payload = input.title.trim() || "无标题";
  await input.persistTitleCommand({
    documentId: input.documentId,
    workspaceId: input.workspaceId,
    title: payload,
  });
  input.notifyDocumentsChanged(input.documentId);
  return payload;
}

export function DocumentContent({
  page,
  openTableId,
  editorHostKind = DEFAULT_EDITOR_HOST_KIND,
}: DocumentContentProps) {
  const documentId = page.identity.documentId;
  const workspaceId = page.identity.workspaceId;
  const initialTitle = page.head.title;
  const updatedAt = page.head.updatedAt;
  const readOnly = page.head.permissions.readOnly;
  const disableDownload = page.head.permissions.disableDownload;
  const disableCopy = page.head.permissions.disableCopy;
  const initialContent = page.body.content;
  const initialStats = page.stats;
  const setCurrentDocument = useCurrentDocumentStore((state) => state.setCurrent);
  const clearIfMatch = useCurrentDocumentStore((state) => state.clearIfMatch);
  const canEditDocument = !readOnly;
  const [pageClientState, dispatchPageClientState] = useReducer(
    pageAggregateClientStateReducer,
    page,
    createPageAggregateClientState,
  );
  const options = pageClientState.options;
  const [stats, setStats] = useState<DocumentStats>(initialStats ?? defaultStats);
  const [history, setHistory] = useState<DocumentSnapshot[]>([]);
  const [historyOpen, setHistoryOpen] = useState(false);
  const editorBridge = useEditorBridgeStore((state) => state.bridge);
  const router = useRouter();
  const showInspector = usePageLayoutStore((state) => state.showInspector);
  const openCommentsForPage = useCommentsUiStore((s) => s.openForPage);
  const spellCheck = useAppPreferencesStore((s) => s.spellCheck);
  const openMoveEmbedPicker = useMoveEmbedPickerStore((s) => s.openPicker);
  const {
    displayTitle: pageTitle,
    committedTitle: committedPageTitle,
    setDraftTitle: setPageTitleDraft,
    commitPersistedTitle,
  } = usePageHeadTitle({
    documentId,
    fallbackTitle: initialTitle,
  });
  const content = pageClientState.content;
  const contentRevision = pageClientState.contentRevision;
  const conflictDetectionKey = pageClientState.conflictDetectionKey;
  const [contentLoading, setContentLoading] = useState(() => initialContent == null);
  const [contentError, setContentError] = useState<string | null>(null);
  const [contentReloadKey, setContentReloadKey] = useState(0);
  const [showContentLoadingIndicator, setShowContentLoadingIndicator] = useState(false);
  const shouldUseRuntimeHost = isLeptosTiptapHostKind(editorHostKind);
  const requestedHostKind = shouldUseRuntimeHost ? editorHostKind : "blocknote";
  const [activeHostKind, setActiveHostKind] = useState<"blocknote" | EditorHostKind>(() =>
    requestedHostKind === "blocknote" ? "blocknote" : requestedHostKind,
  );
  const [hostRuntimeLoadFailure, setHostRuntimeLoadFailure] = useState<string | null>(null);
  const [hostInitFailure, setHostInitFailure] = useState<string | null>(null);
  const [hostCommandFailure, setHostCommandFailure] = useState<string | null>(null);
  const [hostSaveFailure, setHostSaveFailure] = useState<string | null>(null);
  const [hostFallbackCount, setHostFallbackCount] = useState<number>(0);
  const [lastFallbackReason, setLastFallbackReason] = useState<EditorHostFallbackReason | null>(null);
  const [lastFallbackAt, setLastFallbackAt] = useState<string | null>(null);
  const [hostStatus, setHostStatus] = useState<string>("idle");
  const [hostEventAt, setHostEventAt] = useState<string | null>(null);
  const fallbackTriggerHistoryRef = useRef<string[]>([]);
  const shouldStartEditing = canEditDocument;
  const [isEditing, setIsEditing] = useState(() => shouldStartEditing);
  const [keepEditorMounted, setKeepEditorMounted] = useState(() => shouldStartEditing);
  const contentLoadingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const editorUnmountTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const pendingOpenTableRef = useRef<string | null>(null);
  const latestBlocksRef = useRef<Json | null>(null);
  const pageRootRef = useRef<HTMLDivElement>(null);
  const readViewRootRef = useRef<HTMLDivElement>(null);
  const pendingRestoreSnapshotRef = useRef<DocumentSnapshot | null>(null);
  const lastCopyBlockedAtRef = useRef<number>(0);
  const hasRequestedFallbackRef = useRef(false);

  const resetHostObservability = useCallback((nextHost: "blocknote" | EditorHostKind) => {
    setHostStatus(nextHost === "blocknote" ? "blocknote_active" : "booting");
    setHostEventAt(new Date().toISOString());
    setHostRuntimeLoadFailure(null);
    setHostInitFailure(null);
    setHostCommandFailure(null);
    setHostSaveFailure(null);
    setHostFallbackCount(0);
    setLastFallbackReason(null);
    setLastFallbackAt(null);
    fallbackTriggerHistoryRef.current = [];
    hasRequestedFallbackRef.current = false;
  }, []);

  const requestFallbackToBlockNote = useCallback(
    (reason: EditorHostFallbackReason, error?: string | null) => {
      if (hasRequestedFallbackRef.current) {
        return;
      }
      hasRequestedFallbackRef.current = true;
      const now = new Date().toISOString();
      setActiveHostKind("blocknote");
      setHostFallbackCount((prev) => prev + 1);
      setLastFallbackReason(reason);
      setLastFallbackAt(now);
      setHostStatus("blocknote_fallback");
      setHostEventAt(now);
      fallbackTriggerHistoryRef.current = [now, ...fallbackTriggerHistoryRef.current].slice(
        0,
        FALLBACK_TRIGGER_HISTORY_LIMIT,
      );
      if (reason === "runtime_load_failed") {
        setHostRuntimeLoadFailure(error ?? "runtime 加载失败");
      } else if (reason === "host_init_failed") {
        setHostInitFailure(error ?? "host 初始化失败");
      } else if (reason === "command_failed") {
        setHostCommandFailure(error ?? "命令执行失败");
      } else if (reason === "save_failed") {
        setHostSaveFailure(error ?? "保存失败");
      }
    },
    [],
  );

  useEffect(() => {
    setCurrentDocument(documentId, readOnly, disableDownload, disableCopy);
    return () => {
      clearIfMatch(documentId);
    };
  }, [clearIfMatch, disableCopy, disableDownload, documentId, readOnly, setCurrentDocument]);

  useEffect(() => {
    if (!disableCopy) return;

    const toElement = (node: Node | null): Element | null => {
      if (!node) return null;
      if (node.nodeType === Node.TEXT_NODE) {
        return node.parentElement;
      }
      return node instanceof Element ? node : null;
    };

    const isEventInsidePage = () => {
      const root = pageRootRef.current;
      if (!root) return false;
      const selection = typeof window !== "undefined" ? window.getSelection() : null;
      const anchor = selection?.anchorNode ?? null;
      const focus = selection?.focusNode ?? null;
      const anchorEl = toElement(anchor);
      const focusEl = toElement(focus);
      return Boolean((anchorEl && root.contains(anchorEl)) || (focusEl && root.contains(focusEl)));
    };

    const notifyBlocked = () => {
      const now = Date.now();
      if (now - lastCopyBlockedAtRef.current < 1200) return;
      lastCopyBlockedAtRef.current = now;
      window.alert("该页面已禁止复制");
    };

    const onCopy = (event: ClipboardEvent) => {
      if (!isEventInsidePage()) return;
      event.preventDefault();
      event.stopPropagation();
      notifyBlocked();
    };

    const onCut = (event: ClipboardEvent) => {
      if (!isEventInsidePage()) return;
      event.preventDefault();
      event.stopPropagation();
      notifyBlocked();
    };

    const onKeyDown = (event: KeyboardEvent) => {
      if (!isEventInsidePage()) return;
      const key = String(event.key ?? "").toLowerCase();
      const ctrlOrMeta = event.ctrlKey || event.metaKey;
      if (!ctrlOrMeta) return;
      if (key === "c" || key === "x" || key === "insert") {
        event.preventDefault();
        event.stopPropagation();
        notifyBlocked();
      }
    };

    document.addEventListener("copy", onCopy, true);
    document.addEventListener("cut", onCut, true);
    document.addEventListener("keydown", onKeyDown, true);
    return () => {
      document.removeEventListener("copy", onCopy, true);
      document.removeEventListener("cut", onCut, true);
      document.removeEventListener("keydown", onKeyDown, true);
    };
  }, [disableCopy]);

  useEffect(() => {
    setActiveHostKind(requestedHostKind === "blocknote" ? "blocknote" : requestedHostKind);
    resetHostObservability(requestedHostKind === "blocknote" ? "blocknote" : requestedHostKind);
  }, [requestedHostKind, resetHostObservability]);

  useEffect(() => {
    const tableId = (openTableId ?? "").trim();
    if (!tableId) return;
    if (!editorBridge?.openTableFullScreen) return;
    if (pendingOpenTableRef.current === tableId) return;
    pendingOpenTableRef.current = tableId;

    editorBridge.openTableFullScreen(tableId);

    if (typeof window !== "undefined") {
      try {
        const url = new URL(window.location.href);
        url.searchParams.delete("openTableId");
        window.history.replaceState({}, "", url.toString());
      } catch {
        router.replace(`/documents/${documentId}`);
      }
    }
  }, [documentId, editorBridge, openTableId, router]);

  useEffect(() => {
    dispatchPageClientState({
      type: "update_server_page_subtree_title",
      title: committedPageTitle,
    });
  }, [committedPageTitle]);

  useEffect(() => {
    dispatchPageClientState({
      type: "hydrate_from_page",
      page,
    });
  }, [page]);

  useEffect(() => {
    setStats(initialStats ?? defaultStats);
  }, [initialStats]);

  useEffect(() => {
    const nextBlocks = extractPageBlocks(content);
    latestBlocksRef.current = nextBlocks.length > 0 ? (nextBlocks as Json) : null;
  }, [content]);

  useEffect(() => {
    const shouldForceEdit = Boolean((openTableId ?? "").trim()) && canEditDocument;
    if (shouldForceEdit) {
      setIsEditing(true);
      setKeepEditorMounted(true);
    }
  }, [canEditDocument, openTableId]);

  useEffect(() => {
    if (isEditing) {
      if (editorUnmountTimerRef.current) {
        clearTimeout(editorUnmountTimerRef.current);
        editorUnmountTimerRef.current = null;
      }
      setKeepEditorMounted(true);
      return;
    }
    if (editorUnmountTimerRef.current) {
      clearTimeout(editorUnmountTimerRef.current);
    }
    editorUnmountTimerRef.current = setTimeout(() => {
      setKeepEditorMounted(false);
      editorUnmountTimerRef.current = null;
    }, EDITOR_UNMOUNT_GRACE_MS);
    return () => {
      if (editorUnmountTimerRef.current) {
        clearTimeout(editorUnmountTimerRef.current);
        editorUnmountTimerRef.current = null;
      }
    };
  }, [isEditing]);

  useEffect(() => {
    let canceled = false;
    const controller = new AbortController();

    const load = async () => {
      setContentError(null);
      setContentLoading(initialContent == null);
      dispatchPageClientState({
        type: "hydrate_from_page",
        page,
      });
      setShowContentLoadingIndicator(false);

      if (initialContent != null) {
        return;
      }

      if (contentLoadingTimerRef.current) {
        clearTimeout(contentLoadingTimerRef.current);
        contentLoadingTimerRef.current = null;
      }
      contentLoadingTimerRef.current = setTimeout(() => {
        if (!canceled) {
          setShowContentLoadingIndicator(true);
        }
      }, CONTENT_LOADING_DELAY_MS);

      try {
        const response = await fetch(
          `/api/documents/page?documentId=${encodeURIComponent(documentId)}&workspaceId=${encodeURIComponent(workspaceId)}`,
          {
            method: "GET",
            credentials: "include",
            signal: controller.signal,
          },
        );
        if (!response.ok) {
          const payload = await response.json().catch(() => ({}));
          throw new Error(payload?.error ?? "加载页面内容失败");
        }
        const payload = (await response.json()) as {
          page?: PageAggregateProjection;
        };
        const reloadedPage = payload.page ?? null;
        const reloadedBody = reloadedPage?.body ?? null;
        if (canceled) return;
        if (reloadedPage) {
          dispatchPageClientState({
            type: "hydrate_from_page",
            page: {
              ...reloadedPage,
              body: {
                ...reloadedBody,
                content: reloadedBody?.content ?? null,
                revision:
                  typeof reloadedBody?.revision === "number" && Number.isInteger(reloadedBody.revision)
                    ? reloadedBody.revision
                    : 0,
                conflictDetectionKey:
                  typeof reloadedBody?.conflictDetectionKey === "string" && reloadedBody.conflictDetectionKey.trim()
                    ? reloadedBody.conflictDetectionKey
                    : `${documentId}:0`,
              },
            },
          });
        }
      } catch (error) {
        if (canceled) return;
        if ((error as { name?: string })?.name === "AbortError") return;
        setContentError(error instanceof Error ? error.message : "加载页面内容失败");
      } finally {
        if (!canceled) {
          setContentLoading(false);
          setShowContentLoadingIndicator(false);
        }
        if (contentLoadingTimerRef.current) {
          clearTimeout(contentLoadingTimerRef.current);
          contentLoadingTimerRef.current = null;
        }
      }
    };

    void load();

    return () => {
      canceled = true;
      controller.abort();
      if (contentLoadingTimerRef.current) {
        clearTimeout(contentLoadingTimerRef.current);
        contentLoadingTimerRef.current = null;
      }
    };
  }, [contentReloadKey, documentId, initialContent, page, workspaceId]);

  const persistTitle = useCallback(
    async (nextTitle: string) => {
      if (readOnly) return;
      try {
        const payload = await persistPageTitleAndNotifyDocumentsChanged({
          documentId,
          workspaceId,
          title: nextTitle,
          persistTitleCommand: executePageHeadCommand,
          notifyDocumentsChanged: emitDocumentsChanged,
        });
        commitPersistedTitle(payload);
        dispatchPageClientState({
          type: "update_server_page_subtree_title",
          title: payload,
        });
      } catch (error) {
        console.error("更新页面标题失败", error);
      }
    },
    [commitPersistedTitle, documentId, readOnly, workspaceId],
  );

  const handleAiPageHeadTitleChange = useCallback(
    (nextTitle: string) => {
      setPageTitleDraft(nextTitle);
      commitPersistedTitle(nextTitle);
      dispatchPageClientState({
        type: "update_server_page_subtree_title",
        title: nextTitle,
      });
      emitDocumentsChanged(documentId);
      void persistTitle(nextTitle);
    },
    [commitPersistedTitle, documentId, persistTitle, setPageTitleDraft],
  );

  const debouncedPersistTitle = useDebouncedCallback((value: string) => {
    void persistTitle(value);
  }, 600);

  const handleTitleChange = (event: ChangeEvent<HTMLInputElement>) => {
    if (!canEditDocument) return;
    const value = event.target.value;
    setPageTitleDraft(value);
    debouncedPersistTitle(value);
  };

  const handleTitleBlur = () => {
    if (!canEditDocument) return;
    void persistTitle(pageTitle);
  };

  const handleTitleKeyDown = (event: ReactKeyboardEvent<HTMLInputElement>) => {
    if (event.key === "Enter") {
      event.preventDefault();
      event.currentTarget.blur();
    }
  };

  const persistOptions = useCallback(
    async (patch: Partial<PageOptionsState>) => {
      if (readOnly) return;
      try {
        await executePageLayoutCommand({
          documentId,
          workspaceId,
          pageOptions: patch,
        });
      } catch (error) {
        console.error(error);
      }
    },
    [documentId, readOnly, workspaceId],
  );

  const toggleOption = useCallback(
    (key: BooleanPageOptionKey) => {
      if (readOnly) return;
      const nextValue = !options[key];
      dispatchPageClientState({
        type: "patch_page_options",
        patch: { [key]: nextValue } as Partial<PageOptionsState>,
      });
      void persistOptions({ [key]: nextValue } as Partial<PageOptionsState>);
    },
    [options, persistOptions, readOnly],
  );

  const setOptionPatch = useCallback(
    (patch: Partial<PageOptionsState>) => {
      if (readOnly) return;
      dispatchPageClientState({
        type: "patch_page_options",
        patch,
      });
      void persistOptions(patch);
    },
    [persistOptions, readOnly],
  );

  const closeToc = useCallback(() => {
    if (readOnly) return;
    if (!options.showToc) return;
    dispatchPageClientState({
      type: "patch_page_options",
      patch: { showToc: false },
    });
    void persistOptions({ showToc: false });
  }, [options.showToc, persistOptions, readOnly]);

  const handleSetPageFont = useCallback(
    (font: PageFont) => {
      setOptionPatch({ pageFont: font });
    },
    [setOptionPatch],
  );

  const handleSetLayoutDensity = useCallback(
    (density: PageLayoutDensity) => {
      setOptionPatch({ layoutDensity: density });
    },
    [setOptionPatch],
  );

  const handleSetEmbedDefaultToCursor = useCallback(() => {
    if (!canEditDocument) return;
    if (!isEditing) {
      setIsEditing(true);
      return;
    }
    const blockId = editorBridge?.getCursorBlockId?.() ?? null;
    if (!blockId) {
      window.alert("未找到当前光标所在块，请先在编辑器中点击任意块");
      return;
    }
    setOptionPatch({ embedDefaultBlockId: blockId });
    window.alert("已设置“嵌入默认位置”");
  }, [canEditDocument, editorBridge, isEditing, setOptionPatch]);

  const handleClearEmbedDefault = useCallback(() => {
    if (readOnly) return;
    setOptionPatch({ embedDefaultBlockId: null });
    window.alert("已清除“嵌入默认位置”");
  }, [readOnly, setOptionPatch]);

  useEffect(() => {
    const onKeyDown = (event: KeyboardEvent) => {
      const root = pageRootRef.current;
      if (root) {
        const target = event.target;
        if (target && target instanceof Node && !root.contains(target)) {
          return;
        }
      }
      const ctrlOrMeta = event.ctrlKey || event.metaKey;
      if (!ctrlOrMeta) return;
      if (!event.shiftKey) return;
      const key = String(event.key ?? "").toLowerCase();
      if (key === "l") {
        event.preventDefault();
        toggleOption("showToc");
      } else if (key === "c") {
        event.preventDefault();
        toggleOption("protectEditing");
      }
    };
    window.addEventListener("keydown", onKeyDown);
    return () => window.removeEventListener("keydown", onKeyDown);
  }, [toggleOption]);

  const buildDocumentUrl = useCallback((id: string): string => {
    if (typeof window === "undefined" || !window.location) {
      return `/documents/${id}`;
    }
    return `${window.location.origin}/documents/${id}`;
  }, []);

  const copyText = useCallback(async (text: string, successMessage: string) => {
    if (typeof navigator !== "undefined" && navigator.clipboard) {
      try {
        await navigator.clipboard.writeText(text);
        window.alert(successMessage);
        return;
      } catch {
      }
    }
    window.prompt("复制失败，请手动复制内容", text);
  }, []);

  const handleCopyPageLink = useCallback(
    async (includeTitle: boolean) => {
      const url = buildDocumentUrl(documentId);
      if (includeTitle) {
        const text = `${pageTitle || "无标题"}\n${url}`;
        await copyText(text, "标题 + 链接已复制");
        return;
      }
      await copyText(url, "页面链接已复制");
    },
    [buildDocumentUrl, copyText, documentId, pageTitle],
  );

  const handleCopyPageReference = useCallback(
    async (mode: "inline" | "embed") => {
      const template = mode === "inline" ? `((${documentId}))` : `{{${documentId}}}`;
      await copyText(template, mode === "inline" ? "行内引用已复制" : "嵌入引用已复制");
    },
    [copyText, documentId],
  );

  const handleUndo = useCallback(() => {
    if (!isEditing) {
      setIsEditing(true);
      return;
    }
    editorBridge?.undo?.();
  }, [editorBridge, isEditing]);

  const handleRedo = useCallback(() => {
    if (!isEditing) {
      setIsEditing(true);
      return;
    }
    editorBridge?.redo?.();
  }, [editorBridge, isEditing]);

  const handleDeletePage = useCallback(async () => {
    if (readOnly) return;
    const ok = window.confirm("确定删除该页面吗？删除后会进入垃圾桶。");
    if (!ok) return;
    try {
      await deleteDocumentCommand({ documentId, workspaceId });
    } catch (error) {
      const payload = error instanceof Error ? error.message : null;
      window.alert(payload ?? "删除失败");
      return;
    }
    router.push("/");
    router.refresh();
  }, [documentId, readOnly, router, workspaceId]);

  const handleOpenMoveEmbed = useCallback(() => {
    if (readOnly) return;
    openMoveEmbedPicker({
      workspaceId,
      defaultMode: "move",
      modes: ["move", "embed"],
      allowRoot: true,
      excludeIds: [documentId],
      onPick: async (mode, targetId) => {
        if (mode === "move") {
          try {
            await moveDocumentCommand({
              documentId,
              parentId: targetId ?? null,
              position: 999999,
              workspaceId,
            });
          } catch (error) {
            window.alert(error instanceof Error ? error.message : "移动失败");
            return;
          }
          window.alert("移动成功");
          router.refresh();
          return;
        }

        if (!targetId) {
          window.alert("请选择目标页面");
          return;
        }

        try {
          await embedDocumentCommand({
            sourceId: documentId,
            targetId,
          });
        } catch (error) {
          const message = error instanceof Error ? error.message : "嵌入失败";
          window.alert(message);
          return;
        }

        window.alert("已嵌入到目标页面");
      },
    });
  }, [documentId, openMoveEmbedPicker, readOnly, router, workspaceId]);

  const handleAddToTemplates = useCallback(async () => {
    if (readOnly) return;
    const ok = window.confirm("将该页面添加为模板？");
    if (!ok) return;
    const resp = await fetch("/api/documents/template", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ documentId, isTemplate: true }),
    });
    if (!resp.ok) {
      const payload = await resp.json().catch(() => ({}));
      window.alert(payload?.error ?? "设置模板失败");
      return;
    }
    window.alert("已添加为模板");
    router.refresh();
  }, [documentId, readOnly, router]);

  const formattedUpdatedAt = useMemo(() => {
    if (!updatedAt) return "";
    const date = new Date(updatedAt);
    if (Number.isNaN(date.getTime())) return "";
    return new Intl.DateTimeFormat("zh-CN", {
      timeZone: "Asia/Shanghai",
      year: "numeric",
      month: "numeric",
      day: "numeric",
      hour: "2-digit",
      minute: "2-digit",
      second: "2-digit",
      hour12: false,
    }).format(date);
  }, [updatedAt]);

  const handleExport = useCallback(() => {
    if (disableDownload) {
      window.alert("该页面已禁止下载");
      return;
    }
    const latest = history[0];
    const exportBlocks = latest?.blocks ?? latestBlocksRef.current;
    if (!exportBlocks) {
      window.alert("暂无可导出的内容");
      return;
    }
    const payload = JSON.stringify(exportBlocks, null, 2);
    const blob = new Blob([payload], { type: "application/json" });
    const url = URL.createObjectURL(blob);
    const anchor = document.createElement("a");
    anchor.href = url;
    anchor.download = `${pageTitle ?? "未命名页面"}-${new Date().toISOString()}.json`;
    anchor.click();
    URL.revokeObjectURL(url);
  }, [disableDownload, history, pageTitle]);

  const pageRootClass = cn(
    "flex h-full overflow-hidden bg-wolai-bg",
    options.pageFont === "song" && "wolai-page-font-song",
    options.pageFont === "kai" && "wolai-page-font-kai",
    options.layoutDensity === "compact" && "wolai-page-density-compact",
    options.layoutDensity === "spacious" && "wolai-page-density-spacious",
    options.smallText && "wolai-small-text",
    options.hideChildPages && "wolai-hide-child-pages",
  );
  const pageSubtree = useMemo(
    () => selectPageAggregateClientPageSubtree(pageClientState, pageTitle),
    [pageClientState, pageTitle],
  );
  const readViewTocEntries = useMemo(
    () =>
      (pageSubtree?.outline ?? [])
        .filter((entry) => typeof entry.anchorBlockId === "string" && entry.anchorBlockId.trim())
        .map(({ anchorBlockId, level, numbering, title: entryTitle }) => ({
          id: anchorBlockId as string,
          level,
          numbering,
          title: entryTitle,
        })),
    [pageSubtree],
  );
  const getLatestPageAggregateSnapshot = useCallback(
    () =>
      selectPageAggregateClientAiSnapshot(pageClientState, {
        workspaceId,
        pageTitle,
      }),
    [pageClientState, pageTitle, workspaceId],
  );
  const handlePersistedMetaChange = useCallback((meta: PageBodyPersistedMeta) => {
    dispatchPageClientState({
      type: "apply_persisted_body_meta",
      meta,
    });
  }, []);
  const inspectorCanUseEditorBridge = canEditDocument && isEditing;

  const jumpToHeading = useCallback((headingId: string) => {
    const targetRoot = !isEditing ? readViewRootRef.current : pageRootRef.current;
    const target = targetRoot?.querySelector<HTMLElement>(`[data-id="${headingId}"]`);
    if (target) {
      target.scrollIntoView({ behavior: "smooth", block: "center" });
    }
  }, [isEditing]);

  const handleSnapshot = useCallback((payload: { blocks: Json; stats: DocumentStats }) => {
    dispatchPageClientState({
      type: "apply_local_content_snapshot",
      content: payload.blocks,
    });
    latestBlocksRef.current = payload.blocks;
    setHistory((prev) => {
      const now = Date.now();
      if (prev.length > 0 && now - prev[0].timestamp < 4000) {
        return prev;
      }
      const snapshot: DocumentSnapshot = {
        id: `${now}-${Math.random().toString(36).slice(2, 8)}`,
        timestamp: now,
        blocks: payload.blocks,
        stats: payload.stats,
      };
      return [snapshot, ...prev].slice(0, 15);
    });
  }, []);

  const persistStatsRequest = useCallback((next: DocumentStats) => {
    void fetch("/api/documents/stats", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ documentId, workspaceId, stats: next }),
    }).catch((error) => console.error(error));
  }, [documentId, workspaceId]);

  const persistStats = useDebouncedCallback(persistStatsRequest, 1500);

  const handleStatsChange = useCallback(
    (nextStats: DocumentStats) => {
      setStats(nextStats);
      persistStats(nextStats);
    },
    [persistStats],
  );

  const handleRestoreSnapshot = useCallback(
    (snapshot: DocumentSnapshot) => {
      if (!isEditing || !editorBridge) {
        pendingRestoreSnapshotRef.current = snapshot;
        setIsEditing(true);
        return;
      }
      editorBridge.replaceWithSnapshot(snapshot.blocks);
      setHistoryOpen(false);
    },
    [editorBridge, isEditing],
  );

  const handleEnterEditMode = useCallback(() => {
    if (!canEditDocument) return;
    setIsEditing(true);
  }, [canEditDocument]);

  const handleExitEditMode = useCallback(() => {
    setIsEditing(false);
  }, []);

  useEffect(() => {
    if (!isEditing) return;
    if (!editorBridge) return;
    const pendingSnapshot = pendingRestoreSnapshotRef.current;
    if (!pendingSnapshot) return;
    pendingRestoreSnapshotRef.current = null;
    editorBridge.replaceWithSnapshot(pendingSnapshot.blocks);
    setHistoryOpen(false);
  }, [editorBridge, isEditing]);

  const handleHostEvent = useCallback((event: EditorHostEvent) => {
    setHostEventAt(event.at);
    if (event.kind === "status_changed") {
      setHostStatus(event.status);
      return;
    }
    if (event.kind === "runtime_load_failed") {
      setHostRuntimeLoadFailure(event.message);
      return;
    }
    if (event.kind === "host_init_failed") {
      setHostInitFailure(event.message);
      return;
    }
    if (event.kind === "command_failed") {
      setHostCommandFailure(event.message);
      return;
    }
    if (event.kind === "save_failed") {
      setHostSaveFailure(event.message);
      return;
    }
  }, []);

  const hostObservability = useMemo(
    () => ({
      requestedHostKind,
      activeHostKind,
      status: hostStatus,
      runtimeLoadFailed: hostRuntimeLoadFailure,
      hostInitFailed: hostInitFailure,
      commandFailed: hostCommandFailure,
      saveFailed: hostSaveFailure,
      fallbackCount: hostFallbackCount,
      lastFallbackReason,
      lastFallbackAt,
      lastEventAt: hostEventAt,
      fallbackTimestamps: fallbackTriggerHistoryRef.current,
    }),
    [
      activeHostKind,
      hostEventAt,
      hostFallbackCount,
      hostInitFailure,
      hostCommandFailure,
      hostRuntimeLoadFailure,
      hostSaveFailure,
      hostStatus,
      lastFallbackAt,
      lastFallbackReason,
      requestedHostKind,
    ],
  );
  const activeHostFailureMessage =
    hostRuntimeLoadFailure ?? hostInitFailure ?? hostCommandFailure ?? hostSaveFailure;
  const showFallbackBanner =
    requestedHostKind !== "blocknote" && activeHostKind === "blocknote" && lastFallbackReason != null;
  const showFailureBanner =
    requestedHostKind !== "blocknote" &&
    activeHostKind !== "blocknote" &&
    activeHostFailureMessage != null;
  return (
    <ImagePickerProvider documentId={documentId} workspaceId={workspaceId}>
      <div className={pageRootClass} ref={pageRootRef}>
        <div className="flex h-full flex-1 flex-col overflow-hidden">
          <div className="border-b border-wolai-border px-12 pb-6 pt-8">
            <div className="flex items-start justify-between gap-6">
              <div className="min-w-0 flex-1">
                {isEditing && canEditDocument ? (
                  <div className="relative">
                    <input
                      value={pageTitle}
                      onChange={handleTitleChange}
                      onBlur={handleTitleBlur}
                      onKeyDown={handleTitleKeyDown}
                      placeholder="无标题"
                      className="w-full border-none bg-transparent text-3xl font-semibold text-wolai-text-primary outline-none focus:ring-0"
                      aria-label="页面标题"
                      disabled={options.protectEditing || readOnly}
                      spellCheck={spellCheck}
                    />
                  </div>
                ) : (
                  <h1 className="break-words text-3xl font-semibold text-wolai-text-primary">
                    {pageTitle || "无标题"}
                  </h1>
                )}
                {readOnly ? (
                  <p className="mt-1 text-sm text-gray-500">该页面为只读共享，无法编辑。</p>
                ) : options.protectEditing && isEditing ? (
                  <p className="mt-1 text-sm text-[#b91c1c]">当前页面已开启编辑保护，关闭后方可修改内容。</p>
                ) : null}
                <p className="text-sm text-wolai-text-secondary">最近更新：{formattedUpdatedAt}</p>
              </div>
              {canEditDocument && false && (
                <div className="flex shrink-0 items-center gap-2">
                  {isEditing ? (
                    <Button type="button" variant="outline" size="sm" onClick={handleExitEditMode}>
                      返回阅读
                    </Button>
                  ) : (
                    <Button type="button" size="sm" onClick={handleEnterEditMode}>
                      进入编辑
                    </Button>
                  )}
                </div>
              )}
            </div>
          </div>
          <div className="relative flex-1 overflow-y-auto px-12 py-6">
            {contentLoading ? (
              showContentLoadingIndicator ? (
                <div className="flex h-64 items-center justify-center text-sm text-gray-400">
                  页面内容加载中...
                </div>
              ) : (
                <div className="h-64" />
              )
            ) : contentError ? (
              <div className="flex h-64 flex-col items-center justify-center gap-2 text-sm text-red-600">
                <div>{contentError}</div>
                <button
                  type="button"
                  className="rounded-md border border-red-200 bg-red-50 px-3 py-1 text-sm text-red-700 hover:bg-red-100"
                  onClick={() => {
                    setContentError(null);
                    setContentLoading(true);
                    setContentReloadKey((prev) => prev + 1);
                  }}
                >
                  重试
                </button>
              </div>
            ) : (
              <div className="relative">
                {showFallbackBanner ? (
                  <div className="mb-4 flex items-center justify-between rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
                    <div>
                      <div className="font-medium">
                        默认 island 主编辑器已自动回退到 BlockNote
                      </div>
                      <div className="mt-1 text-xs text-amber-700">
                        原因：{lastFallbackReason}
                        {lastFallbackAt ? `，时间：${lastFallbackAt}` : ""}
                      </div>
                    </div>
                    <Button
                      type="button"
                      size="sm"
                      variant="outline"
                      onClick={() => {
                        setActiveHostKind(requestedHostKind);
                        resetHostObservability(requestedHostKind);
                      }}
                    >
                      重试 island 主链
                    </Button>
                  </div>
                ) : null}
                {showFailureBanner ? (
                  <div className="mb-4 flex items-center justify-between rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
                    <div>
                      <div className="font-medium">island 主链出现错误</div>
                      <div className="mt-1 text-xs text-red-600">{activeHostFailureMessage}</div>
                    </div>
                    <Button
                      type="button"
                      size="sm"
                      variant="outline"
                      onClick={() => requestFallbackToBlockNote("explicit_fallback", activeHostFailureMessage)}
                    >
                      切回 BlockNote
                    </Button>
                  </div>
                ) : null}
                {keepEditorMounted && (
                  <div className={cn(!isEditing && "pointer-events-none absolute inset-0 opacity-0")} aria-hidden={!isEditing}>
                    {activeHostKind !== "blocknote" ? (
                      <EditorHost
                        documentId={documentId}
                        workspaceId={workspaceId}
                        initialContent={content}
                        title={pageTitle}
                        hostKind={activeHostKind}
                        initialRevision={contentRevision}
                        initialConflictDetectionKey={conflictDetectionKey}
                        pageOptions={options}
                        readOnly={readOnly}
                        onStatsChange={handleStatsChange}
                        onSnapshot={handleSnapshot}
                        onCloseToc={closeToc}
                        onPersistedMetaChange={(meta: { revision: number | null; conflictDetectionKey: string | null }) => {
                          handlePersistedMetaChange(meta);
                        }}
                        onHostEvent={handleHostEvent}
                        onRequestFallback={(payload) => {
                          requestFallbackToBlockNote(payload.reason, payload.error);
                        }}
                      />
                    ) : (
                      <BlockNoteEditor
                        documentId={documentId}
                        workspaceId={workspaceId}
                        initialContent={content}
                        title={pageTitle}
                        initialRevision={contentRevision}
                        initialConflictDetectionKey={conflictDetectionKey}
                        pageOptions={options}
                        readOnly={readOnly}
                        onStatsChange={handleStatsChange}
                        onSnapshot={handleSnapshot}
                        onCloseToc={closeToc}
                        onPersistedMetaChange={(meta: { revision: number | null; conflictDetectionKey: string | null }) => {
                          handlePersistedMetaChange(meta);
                        }}
                      />
                    )}
                  </div>
                )}
                <div
                  className="sr-only"
                  data-editor-host-observability={JSON.stringify(hostObservability)}
                  data-editor-host-active={hostObservability.activeHostKind}
                  data-editor-host-requested={hostObservability.requestedHostKind}
                  data-editor-host-status={hostObservability.status}
                  data-editor-host-runtime-load-failed={
                    hostObservability.runtimeLoadFailed ? "1" : "0"
                  }
                  data-editor-host-init-failed={hostObservability.hostInitFailed ? "1" : "0"}
                  data-editor-host-command-failed={hostObservability.commandFailed ? "1" : "0"}
                  data-editor-host-save-failed={hostObservability.saveFailed ? "1" : "0"}
                  data-editor-host-fallback-count={String(hostObservability.fallbackCount)}
                  data-editor-host-last-fallback-reason={hostObservability.lastFallbackReason ?? ""}
                />
                {!isEditing && (
                  <div className="relative" ref={readViewRootRef}>
                    <DocumentReadView
                      content={content}
                      documentId={documentId}
                      options={options}
                      pageSubtree={pageSubtree}
                      className="mx-auto w-full max-w-[980px]"
                    />
                    <DocumentToc
                      entries={readViewTocEntries}
                      visible={options.showToc}
                      onJump={jumpToHeading}
                      onClose={closeToc}
                    />
                  </div>
                )}
              </div>
            )}
            <PageBacklinksPanel
              className="mt-10"
              workspaceId={workspaceId}
              documentId={documentId}
              defaultCollapsed={options.collapseBacklinks}
            />
          </div>
        </div>
        {showInspector && (
          <PageOptionsSidebar
            documentId={documentId}
            options={options}
            stats={stats}
            onToggle={toggleOption}
            onSetPageFont={handleSetPageFont}
            onSetLayoutDensity={handleSetLayoutDensity}
            onSetEmbedDefaultToCursor={inspectorCanUseEditorBridge ? handleSetEmbedDefaultToCursor : undefined}
            onClearEmbedDefault={handleClearEmbedDefault}
            onExport={handleExport}
            onOpenHistory={() => setHistoryOpen(true)}
            onOpenComments={() => openCommentsForPage({ workspaceId, documentId })}
            onUndo={canEditDocument ? handleUndo : undefined}
            onRedo={canEditDocument ? handleRedo : undefined}
            onDeletePage={canEditDocument ? handleDeletePage : undefined}
            onOpenMoveEmbedPicker={canEditDocument ? handleOpenMoveEmbed : undefined}
            onCopyPageLink={handleCopyPageLink}
            onCopyPageReference={handleCopyPageReference}
            onAddToTemplates={canEditDocument ? handleAddToTemplates : undefined}
          />
        )}
      </div>
      <DocumentHistoryDrawer
        open={historyOpen}
        onOpenChange={setHistoryOpen}
        history={history}
        onRestore={handleRestoreSnapshot}
      />
      <MoveEmbedPickerHost />
      <DocumentCommentsDrawer />
      <DocumentAiAgentPanel
        documentId={documentId}
        getLatestPageAggregateSnapshot={getLatestPageAggregateSnapshot}
        onPersistedMetaChange={handlePersistedMetaChange}
        onPageHeadTitleChange={handleAiPageHeadTitleChange}
      />
    </ImagePickerProvider>
  );
}
