"use client";

import { memo, useMemo, useRef, useCallback, useState, useEffect } from "react";
import { createPortal } from "react-dom";
import { useTranslations } from "next-intl";
import { NodeProps } from "@xyflow/react";

import type { NodeAnchor, TextbookContentNode as TextbookContentNodeModel } from "../../types";
import {
  injectPlaceholders,
  parseAnchoredText,
  toCircledNumber,
  getNextPointIndex,
  markdownToPlainText,
} from "../../lib/anchor-injector";
import { getNodeColor } from "../../lib/node-summary";
import { AnchorNodeSelector } from "./anchor-node-selector";
import { renderSegments } from "./textbook-segments";

interface TextbookContentNodeProps {
  node: TextbookContentNodeModel;
  anchors: NodeAnchor[];
  selectedNodeId: string | null;
  /** 可锚定的教学节点列表（用于锚点节点选择器）*/
  anchorableNodes?: { id: string; title: string; type: string }[];
  onAddRangeAnchor?: (params: {
    nodeId: string;
    start: number;
    end: number;
    textPreview: string;
  }) => void;
  onAddPointAnchor?: (params: {
    nodeId: string;
    start: number;
  }) => void;
  /** 创建新节点并锚定 */
  onCreateNewNode?: (params: {
    anchorType: "range" | "point";
    start: number;
    end?: number;
    textPreview?: string;
  }) => void;
  onSelectNode?: (id: string | null) => void;
  onResize?: (width: number, height: number) => void;
}

/**
 * 类型守卫：安全收窄 React Flow NodeProps.data 为 TextbookContentNodeProps
 * 替代 `as unknown as` 断言，通过结构检查确保数据形状正确
 */
function isTextbookContentNodePropsData(
  data: unknown,
): data is TextbookContentNodeProps {
  if (typeof data !== "object" || data === null) return false;
  const obj = data as Record<string, unknown>;
  return (
    typeof obj.node === "object" &&
    obj.node !== null &&
    typeof (obj.node as Record<string, unknown>).type === "string" &&
    Array.isArray(obj.anchors)
  );
}

export const TextbookContentNode = memo(function TextbookContentNode({
  data,
  selected,
}: NodeProps) {
  const t = useTranslations("lessonPreparation");
  const props = isTextbookContentNodePropsData(data) ? data : null;

  const contentRef = useRef<HTMLDivElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const resizeHandleRef = useRef<HTMLDivElement>(null);
  const [showAnchorMenu, setShowAnchorMenu] = useState<{
    x: number;
    y: number;
    selection: { start: number; end: number; text: string } | null;
    point: number | null;
  } | null>(null);
  // 光标位置指示器（左键点击时显示）
  const [cursorPos, setCursorPos] = useState<{ x: number; y: number } | null>(null);
  // 拖拽缩放状态
  const [resizing, setResizing] = useState(false);
  const resizeStart = useRef<{ x: number; y: number; w: number; h: number } | null>(null);

  const node = props?.node;
  const anchors = useMemo(() => props?.anchors ?? [], [props?.anchors]);
  const selectedNodeId = props?.selectedNodeId ?? null;
  const anchorableNodes = props?.anchorableNodes ?? [];

  // 注入锚点标记后的 Markdown
  const injectedContent = useMemo(() => {
    if (!node) return "";
    return injectPlaceholders(node.data.content, anchors);
  }, [node, anchors]);

  // 解析为段落数组（用于自定义渲染）
  const segments = useMemo(
    () => parseAnchoredText(injectedContent),
    [injectedContent],
  );

  // 选中节点的激活锚点 ID 集合
  const activeAnchorIds = useMemo(() => {
    if (!selectedNodeId) return new Set<string>();
    return new Set(
      anchors.filter((a) => a.nodeId === selectedNodeId).map((a) => a.id),
    );
  }, [anchors, selectedNodeId]);

  // 获取锚点对应的节点颜色
  const getAnchorNodeColor = useCallback(
    (anchorId: string): string => {
      const anchor = anchors.find((a) => a.id === anchorId);
      if (!anchor) return "#9e9e9e";
      return getNodeColor(anchor.nodeId);
    },
    [anchors],
  );

  // 计算选中文本在纯文本中的偏移
  const computeSelectionOffset = useCallback(
    (selectedText: string): { start: number; end: number } | null => {
      if (!node) return null;
      const plainText = markdownToPlainText(node.data.content);
      const start = plainText.indexOf(selectedText);
      if (start >= 0) {
        return { start, end: start + selectedText.length };
      }
      return null;
    },
    [node],
  );

  // 计算点击位置在纯文本中的偏移，并返回 caret 的视口坐标（用于精确定位光标指示器）
  const computePointOffset = useCallback(
    (clientX: number, clientY: number): { offset: number; rect: DOMRect | null } => {
      let offset = -1;
      let rect: DOMRect | null = null;
      // 优先用 caretPositionFromPoint（标准 API）
      if (document.caretPositionFromPoint) {
        const pos = document.caretPositionFromPoint(clientX, clientY);
        if (pos) {
          offset = pos.offset;
          try {
            const range = document.createRange();
            range.setStart(pos.offsetNode, pos.offset);
            range.setEnd(pos.offsetNode, pos.offset);
            // collapsed range 用 getClientRects 获取 caret 位置
            const rects = range.getClientRects();
            rect = rects.length > 0 ? rects[0] : range.getBoundingClientRect();
          } catch {
            rect = null;
          }
        }
      } else if (document.caretRangeFromPoint) {
        // 回退：WebKit 专用 API
        const range = document.caretRangeFromPoint(clientX, clientY);
        if (range) {
          offset = range.startOffset;
          const rects = range.getClientRects();
          rect = rects.length > 0 ? rects[0] : range.getBoundingClientRect();
        }
      }
      return { offset, rect };
    },
    [],
  );

  // 右键菜单：在右键位置弹出锚点菜单
  const handleContextMenu = useCallback(
    (e: React.MouseEvent) => {
      if (!node) return;
      e.preventDefault();
      e.stopPropagation();

      const selection = window.getSelection();
      const selectedText = selection && !selection.isCollapsed ? selection.toString() : "";

      if (selectedText) {
        // 有选中文本：提供区间锚定
        const offsets = computeSelectionOffset(selectedText);
        if (!offsets) return;
        setShowAnchorMenu({
          x: e.clientX,
          y: e.clientY,
          selection: { ...offsets, text: selectedText },
          point: null,
        });
      } else {
        // 无选中文本：提供点锚定
        const { offset } = computePointOffset(e.clientX, e.clientY);
        if (offset < 0) return;
        setShowAnchorMenu({
          x: e.clientX,
          y: e.clientY,
          selection: null,
          point: offset,
        });
      }
    },
    [node, computeSelectionOffset, computePointOffset],
  );

  // 左键点击：显示光标位置指示器（用 caret 实际坐标精确定位）
  const handleClick = useCallback(
    (e: React.MouseEvent) => {
      if (!node) return;
      // 如果有选中文本，不显示光标（让浏览器处理选择）
      const selection = window.getSelection();
      if (selection && !selection.isCollapsed) {
        setCursorPos(null);
        return;
      }

      // 计算 caret 位置，优先用 caret 的 rect 坐标，fallback 到鼠标坐标
      const { offset, rect } = computePointOffset(e.clientX, e.clientY);
      if (offset < 0) {
        setCursorPos(null);
        return;
      }

      setCursorPos({
        x: rect && rect.width >= 0 ? rect.left : e.clientX,
        y: rect && rect.width >= 0 ? rect.top : e.clientY,
      });
    },
    [node, computePointOffset],
  );

  // 关闭锚点菜单
  useEffect(() => {
    if (!showAnchorMenu) return;
    function handleOutside(e: MouseEvent) {
      const target = e.target as HTMLElement;
      if (!target.closest("[data-anchor-menu]")) {
        setShowAnchorMenu(null);
      }
    }
    document.addEventListener("mousedown", handleOutside);
    return () => document.removeEventListener("mousedown", handleOutside);
  }, [showAnchorMenu]);

  // 光标指示器自动消失
  useEffect(() => {
    if (!cursorPos) return;
    const timer = setTimeout(() => setCursorPos(null), 2000);
    return () => clearTimeout(timer);
  }, [cursorPos]);

  // 阻止 React Flow 在正文内容区和缩放手柄上拦截 pointerdown 事件（切实保障文本选择和缩放可用）
  // nodrag class 只能阻止拖拽，但 React Flow 可能在更上层 preventDefault 阻止文本选择
  // 使用原生事件监听器 stopPropagation，让 React Flow 完全收不到 pointerdown
  useEffect(() => {
    const stopPointer = (e: PointerEvent) => {
      e.stopPropagation();
    };
    const contentEl = contentRef.current;
    const resizeEl = resizeHandleRef.current;
    contentEl?.addEventListener("pointerdown", stopPointer);
    resizeEl?.addEventListener("pointerdown", stopPointer);
    return () => {
      contentEl?.removeEventListener("pointerdown", stopPointer);
      resizeEl?.removeEventListener("pointerdown", stopPointer);
    };
  }, []);

  // 拖拽缩放
  const handleResizeStart = useCallback(
    (e: React.MouseEvent) => {
      e.preventDefault();
      e.stopPropagation();
      if (!containerRef.current) return;
      const rect = containerRef.current.getBoundingClientRect();
      resizeStart.current = { x: e.clientX, y: e.clientY, w: rect.width, h: rect.height };
      setResizing(true);
    },
    [],
  );

  useEffect(() => {
    if (!resizing || !resizeStart.current || !containerRef.current) return;
    function handleMove(e: MouseEvent) {
      if (!resizeStart.current || !containerRef.current) return;
      const dx = e.clientX - resizeStart.current.x;
      const dy = e.clientY - resizeStart.current.y;
      const newW = Math.max(300, resizeStart.current.w + dx);
      const newH = Math.max(200, resizeStart.current.h + dy);
      containerRef.current.style.width = `${newW}px`;
      containerRef.current.style.height = `${newH}px`;
    }
    function handleUp() {
      setResizing(false);
      resizeStart.current = null;
    }
    document.addEventListener("mousemove", handleMove);
    document.addEventListener("mouseup", handleUp);
    return () => {
      document.removeEventListener("mousemove", handleMove);
      document.removeEventListener("mouseup", handleUp);
    };
  }, [resizing]);

  if (!node) {
    return (
      <div className="rounded-lg border-2 border-outline-variant bg-surface p-4 text-on-surface-variant">
        {t("editor.textbookContentMissing")}
      </div>
    );
  }

  const nextPointNumber = getNextPointIndex(anchors);

  return (
    <div
      ref={containerRef}
      className="rounded-lg border-2 bg-surface shadow-lg flex flex-col relative"
      style={{
        borderColor: selected ? "#1976d2" : "#455a64",
        boxShadow: selected ? "0 0 0 2px rgba(25,118,210,0.3)" : undefined,
        width: 520,
        minWidth: 300,
        minHeight: 200,
      }}
    >
      {/* 头部 */}
      <div
        className="px-3 py-2 rounded-t-md text-white text-xs font-medium flex items-center justify-between flex-shrink-0"
        style={{ backgroundColor: "#455a64" }}
      >
        <span>{t("editor.textbookContent")}</span>
        <span className="text-white/60 text-[10px]">
          {t("editor.rightClickHint")}
        </span>
      </div>

      {/* 正文内容 */}
      <div
        ref={contentRef}
        // nodrag class 让 React Flow 跳过拖拽逻辑，允许在正文上选择文本
        className="px-4 py-3 flex-1 overflow-y-auto text-sm leading-relaxed text-on-surface select-text nodrag"
        onContextMenu={handleContextMenu}
        onClick={handleClick}
        style={{ userSelect: "text", WebkitUserSelect: "text" }}
      >
        {node.data.content ? (
          <div className="whitespace-pre-wrap break-words">
            {renderSegments({
              segments,
              activeAnchorIds,
              getAnchorNodeColor,
              onSelectNode: props?.onSelectNode,
              anchors,
            })}
          </div>
        ) : (
          <div className="text-on-surface-variant text-sm py-8 text-center">
            {t("editor.textbookContentEmpty")}
          </div>
        )}
      </div>

      {/* 拖拽缩放手柄 */}
      <div
        ref={resizeHandleRef}
        // nodrag class 让 React Flow 跳过拖拽逻辑，允许缩放手柄独立工作
        className="absolute bottom-0 right-0 w-5 h-5 cursor-nwse-resize bg-surface-container-high border-l-2 border-t-2 border-outline-variant rounded-tl-md flex items-center justify-center hover:bg-surface-container-highest z-10 nodrag"
        onMouseDown={handleResizeStart}
        title={t("editor.dragToResize")}
      >
        <svg width="8" height="8" viewBox="0 0 8 8" className="text-on-surface-variant">
          <path d="M0 8 L8 0 M3 8 L8 3 M6 8 L8 6" stroke="currentColor" strokeWidth="1" fill="none" />
        </svg>
      </div>

      {/* 光标位置指示器（通过 portal 渲染到 body，避免 React Flow transform 容器影响 fixed 定位）*/}
      {cursorPos && typeof document !== "undefined" && createPortal(
        <div
          className="fixed pointer-events-none z-40"
          style={{
            left: cursorPos.x,
            top: cursorPos.y,
            width: 2,
            height: 16,
            backgroundColor: "#1976d2",
            animation: "cursor-blink 1s infinite",
          }}
        />,
        document.body,
      )}

      {/* 锚点浮动菜单（右键触发，通过 portal 渲染到 body 保证 fixed 定位相对视口）*/}
      {showAnchorMenu && typeof document !== "undefined" && createPortal(
        <div
          data-anchor-menu
          className="fixed z-50 bg-surface border border-outline-variant rounded-lg shadow-xl p-2 min-w-[220px] max-h-[60vh] overflow-y-auto"
          style={{
            left: showAnchorMenu.x,
            top: showAnchorMenu.y,
          }}
        >
          {showAnchorMenu.selection ? (
            <div>
              <div className="text-xs text-on-surface-variant mb-2 px-2">
                {t("editor.rangeAnchorTitle")}
              </div>
              <AnchorNodeSelector
                t={t}
                anchorableNodes={anchorableNodes}
                hasSelectedNode={!!selectedNodeId}
                onPickNode={(nodeId) => {
                  if (props?.onAddRangeAnchor && showAnchorMenu.selection) {
                    props.onAddRangeAnchor({
                      nodeId,
                      start: showAnchorMenu.selection.start,
                      end: showAnchorMenu.selection.end,
                      textPreview: showAnchorMenu.selection.text,
                    });
                  }
                  setShowAnchorMenu(null);
                }}
                onCreateNew={() => {
                  if (props?.onCreateNewNode && showAnchorMenu.selection) {
                    props.onCreateNewNode({
                      anchorType: "range",
                      start: showAnchorMenu.selection.start,
                      end: showAnchorMenu.selection.end,
                      textPreview: showAnchorMenu.selection.text,
                    });
                  }
                  setShowAnchorMenu(null);
                }}
              />
            </div>
          ) : showAnchorMenu.point !== null ? (
            <div>
              <div className="text-xs text-on-surface-variant mb-2 px-2">
                {t("editor.pointAnchorTitle", { number: toCircledNumber(nextPointNumber) })}
              </div>
              <AnchorNodeSelector
                t={t}
                anchorableNodes={anchorableNodes}
                hasSelectedNode={!!selectedNodeId}
                onPickNode={(nodeId) => {
                  if (props?.onAddPointAnchor && showAnchorMenu.point !== null) {
                    props.onAddPointAnchor({
                      nodeId,
                      start: showAnchorMenu.point,
                    });
                  }
                  setShowAnchorMenu(null);
                }}
                onCreateNew={() => {
                  if (props?.onCreateNewNode && showAnchorMenu.point !== null) {
                    props.onCreateNewNode({
                      anchorType: "point",
                      start: showAnchorMenu.point,
                    });
                  }
                  setShowAnchorMenu(null);
                }}
              />
            </div>
          ) : null}
        </div>,
        document.body,
      )}
    </div>
  );
});

