"use client"

/**
 * Obsidian 风格的知识点力导向图谱。
 *
 * 视觉对齐 Obsidian Graph View：
 * - 小圆点节点（非大圆球），半径随连接数轻微变化
 * - 极细半透明边线，父子/依赖用颜色区分（Obsidian 无虚线）
 * - 标签默认显示在节点右侧，缩放小时按连接数优先显示
 * - 选中/hover 节点时高亮邻居，无关节点淡化
 * - 节点拖拽联动（d3-force 物理模拟）
 *
 * 通过 next/dynamic ssr:false 加载，避免 SSR 阶段访问 canvas/window。
 * 顶部用 `import type` 引入类型，编译期擦除，防止模块顶层副作用在 SSR 执行。
 */

import { useCallback, useEffect, useMemo, useRef, useState, type ComponentType } from "react"
import dynamic from "next/dynamic"
import type ForceGraph2DType from "react-force-graph-2d"
import type {
  ForceGraphMethods,
  NodeObject,
  LinkObject,
} from "react-force-graph-2d"
import { useTranslations } from "next-intl"
import { Share2 } from "lucide-react"
import { EmptyState } from "@/shared/components/ui/empty-state"
import type {
  GraphViewMode,
  KnowledgeGraphData,
  KpWithRelations,
  MasteryInfo,
  MasteryLevel,
} from "../types"

/** 章节颜色调色板（柔和、Obsidian 风格） */
const CHAPTER_COLORS = [
  "#7c3aed", "#2563eb", "#dc2626", "#059669",
  "#d97706", "#db2777", "#0891b2", "#65a30d",
  "#9333ea", "#0284c7", "#e11d48", "#16a34a",
]

/** 掌握度色彩（柔和） */
const MASTERY_FILL: Record<MasteryLevel, string> = {
  low: "#dc2626",
  medium: "#d97706",
  high: "#059669",
  unassessed: "#94a3b8",
}

/** 边颜色：父子关系（中性灰）/ 前置依赖（紫色） */
const EDGE_PARENT_COLOR = "rgba(148, 163, 184, 0.5)"
const EDGE_PREREQ_COLOR = "rgba(124, 58, 237, 0.6)"

/** 力导向布局参数（参照 Obsidian 默认手感）。
 * Obsidian 用较强斥力让节点分散，链接距离短，collide 防重叠。
 */
const FORCE_CHARGE = -280
const FORCE_LINK_DISTANCE = 45
const FORCE_CENTER_STRENGTH = 0.02

/** 节点基础半径（Obsidian 风格：小圆点） */
const NODE_BASE_RADIUS = 4
const NODE_MAX_RADIUS = 9
/** 标签显示的缩放阈值（缩放小于此值时只显示高连接数节点标签） */
const LABEL_MIN_SCALE = 1.2
/** 缩放小于此值时不显示任何标签 */
const LABEL_HIDE_SCALE = 0.6

/**
 * 动态加载的 react-force-graph-2d（SSR 关闭）。
 * 用 `as unknown as ComponentType<...>` 保留完整 props 类型。
 */
const ForceGraph2D = dynamic(
  () => import("react-force-graph-2d").then((mod) => mod.default),
  {
    ssr: false,
    loading: () => <ForceGraphLoading />,
  },
) as unknown as ComponentType<React.ComponentProps<typeof ForceGraph2DType>>

function ForceGraphLoading() {
  const t = useTranslations("textbooks")
  return (
    <div className="h-full flex items-center justify-center text-sm text-muted-foreground">
      {t("reader.loadingKnowledge")}
    </div>
  )
}

function getMasteryLevel(mastery: MasteryInfo | null): MasteryLevel {
  if (!mastery) return "unassessed"
  if (mastery.masteryLevel < 60) return "low"
  if (mastery.masteryLevel < 85) return "medium"
  return "high"
}

/** 力导向图谱节点 */
interface KpGraphNode extends NodeObject {
  id: string
  name: string
  kp: KpWithRelations
  mastery: MasteryInfo | null
  /** 连接数（决定节点大小） */
  val: number
  /** 章节颜色 */
  chapterColor: string
}

/** 力导向图谱边 */
interface KpGraphLink {
  source: string | KpGraphNode
  target: string | KpGraphNode
  /** 边类型：parent（树归属）/ prerequisite（依赖） */
  edgeType: "parent" | "prerequisite"
}

interface ForceGraphProps {
  data: KnowledgeGraphData
  viewMode: GraphViewMode
  searchText: string
  selectedKpId: string | null
  onSelectKp: (kpId: string | null) => void
}

/** 将业务数据转换为 react-force-graph-2d graphData 格式 */
function buildGraphData(
  data: KnowledgeGraphData,
): { nodes: KpGraphNode[]; links: KpGraphLink[] } {
  const kpIdSet = new Set(data.knowledgePoints.map((kp) => kp.id))

  // 章节颜色映射
  const chapterColorMap = new Map<string, string>()
  const chapterIds = [...new Set(
    data.knowledgePoints
      .map((kp) => kp.chapterId)
      .filter((id): id is string => id !== null),
  )]
  chapterIds.forEach((id, index) => {
    chapterColorMap.set(id, CHAPTER_COLORS[index % CHAPTER_COLORS.length]!)
  })

  // 计算每个节点的连接数
  const connectionCount = new Map<string, number>()
  for (const kp of data.knowledgePoints) {
    if (kp.parentId && kpIdSet.has(kp.parentId)) {
      connectionCount.set(kp.id, (connectionCount.get(kp.id) ?? 0) + 1)
      connectionCount.set(kp.parentId, (connectionCount.get(kp.parentId) ?? 0) + 1)
    }
    for (const prereqId of kp.prerequisiteIds) {
      if (kpIdSet.has(prereqId)) {
        connectionCount.set(kp.id, (connectionCount.get(kp.id) ?? 0) + 1)
        connectionCount.set(prereqId, (connectionCount.get(prereqId) ?? 0) + 1)
      }
    }
  }

  const nodes: KpGraphNode[] = data.knowledgePoints.map((kp) => ({
    id: kp.id,
    name: kp.name,
    kp,
    mastery: data.masteryMap[kp.id] ?? null,
    val: connectionCount.get(kp.id) ?? 0,
    chapterColor: chapterColorMap.get(kp.chapterId ?? "") ?? "#6b7280",
  }))

  const links: KpGraphLink[] = []
  for (const kp of data.knowledgePoints) {
    if (kp.parentId && kpIdSet.has(kp.parentId)) {
      links.push({ source: kp.parentId, target: kp.id, edgeType: "parent" })
    }
    for (const prereqId of kp.prerequisiteIds) {
      if (kpIdSet.has(prereqId)) {
        links.push({ source: prereqId, target: kp.id, edgeType: "prerequisite" })
      }
    }
  }

  return { nodes, links }
}

/** 计算节点邻居集合（含自身） */
function computeNeighbors(data: KnowledgeGraphData, kpId: string): Set<string> {
  const result = new Set<string>([kpId])
  const selectedKp = data.knowledgePoints.find((kp) => kp.id === kpId)
  if (selectedKp) {
    if (selectedKp.parentId) result.add(selectedKp.parentId)
    for (const id of selectedKp.prerequisiteIds) result.add(id)
    for (const kp of data.knowledgePoints) {
      if (kp.parentId === kpId) result.add(kp.id)
      if (kp.prerequisiteIds.includes(kpId)) result.add(kp.id)
    }
  }
  return result
}

function getNodeHighlightState(
  nodeId: string,
  searchText: string,
  matchedIds: Set<string>,
  selectedKpId: string | null,
  neighborIds: Set<string>,
): "highlighted" | "dimmed" | "normal" {
  if (searchText) {
    return matchedIds.has(nodeId) ? "highlighted" : "dimmed"
  }
  if (selectedKpId) {
    return neighborIds.has(nodeId) ? "highlighted" : "dimmed"
  }
  return "normal"
}

/** 节点半径：基础 + 连接数轻微缩放（Obsidian 风格，变化幅度小） */
function getNodeRadius(val: number): number {
  const scaled = NODE_BASE_RADIUS + Math.log2(val + 1) * 1.8
  return Math.min(NODE_MAX_RADIUS, scaled)
}

function ForceGraphInner({
  data,
  viewMode,
  searchText,
  selectedKpId,
  onSelectKp,
}: ForceGraphProps) {
  const t = useTranslations("textbooks")
  const containerRef = useRef<HTMLDivElement>(null)
  const fgRef = useRef<ForceGraphMethods | undefined>(undefined)
  const [hoveredKpId, setHoveredKpId] = useState<string | null>(null)
  const [dimensions, setDimensions] = useState({ width: 800, height: 600 })

  const graphData = useMemo(() => buildGraphData(data), [data])

  // 搜索匹配
  const matchedIds = useMemo(() => {
    if (!searchText) return new Set<string>()
    const searchLower = searchText.toLowerCase()
    return new Set(
      data.knowledgePoints
        .filter((kp) => kp.name.toLowerCase().includes(searchLower))
        .map((kp) => kp.id),
    )
  }, [searchText, data])

  // 选中节点的邻居集合
  const selectedNeighborIds = useMemo(() => {
    if (!selectedKpId) return new Set<string>()
    return computeNeighbors(data, selectedKpId)
  }, [selectedKpId, data])

  // hover 节点的邻居集合（Obsidian 风格：hover 也高亮邻居）
  const hoveredNeighborIds = useMemo(() => {
    if (!hoveredKpId) return new Set<string>()
    return computeNeighbors(data, hoveredKpId)
  }, [hoveredKpId, data])

  // 当前生效的高亮邻居集合（选中优先于 hover）
  const activeNeighborIds = selectedKpId ? selectedNeighborIds : hoveredNeighborIds
  const activeFocusId = selectedKpId ?? hoveredKpId

  // 容器尺寸自适应
  useEffect(() => {
    const container = containerRef.current
    if (!container) return
    const observer = new ResizeObserver((entries) => {
      const entry = entries[0]
      if (entry) {
        setDimensions({
          width: Math.max(320, entry.contentRect.width),
          height: Math.max(240, entry.contentRect.height),
        })
      }
    })
    observer.observe(container)
    return () => observer.disconnect()
  }, [])

  // 配置力（Obsidian 风格）
  useEffect(() => {
    const fg = fgRef.current
    if (!fg) return
    fg.d3Force("charge")?.strength(FORCE_CHARGE)
    fg.d3Force("link")?.distance(FORCE_LINK_DISTANCE)
    fg.d3Force("center")?.strength(FORCE_CENTER_STRENGTH)
    fg.d3ReheatSimulation()
  }, [graphData])

  // 节点 Canvas 绘制（Obsidian 风格）
  const nodeCanvasObject = useCallback(
    (node: NodeObject, ctx: CanvasRenderingContext2D, globalScale: number) => {
      const kpNode = node as KpGraphNode
      const highlightState = getNodeHighlightState(
        kpNode.id,
        searchText,
        matchedIds,
        activeFocusId,
        activeNeighborIds,
      )

      const radius = getNodeRadius(kpNode.val)
      const showMastery = viewMode === "student-mastery" || viewMode === "class-mastery"
      const fillColor = showMastery
        ? MASTERY_FILL[getMasteryLevel(kpNode.mastery)]
        : kpNode.chapterColor

      // 透明度（Obsidian 风格：淡化节点保持可见但低对比）
      const alpha = highlightState === "dimmed" ? 0.15 : 1

      ctx.save()
      ctx.globalAlpha = alpha

      // 选中/hover 节点外圈高亮环（Obsidian 风格）
      if (kpNode.id === activeFocusId) {
        ctx.beginPath()
        ctx.arc(kpNode.x ?? 0, kpNode.y ?? 0, radius + 3, 0, 2 * Math.PI)
        ctx.strokeStyle = "rgba(124, 58, 237, 0.8)"
        ctx.lineWidth = 1.5
        ctx.stroke()
      }

      // 主圆（Obsidian 风格：实心小圆点 + 轻微白色描边）
      ctx.beginPath()
      ctx.arc(kpNode.x ?? 0, kpNode.y ?? 0, radius, 0, 2 * Math.PI)
      ctx.fillStyle = fillColor
      ctx.fill()
      ctx.strokeStyle = "rgba(255,255,255,0.9)"
      ctx.lineWidth = 0.8
      ctx.stroke()

      // 标签（Obsidian 风格：节点右侧，缩放小时只显示高连接数节点）
      const shouldShowLabel = (() => {
        if (globalScale < LABEL_HIDE_SCALE) return false
        if (highlightState === "dimmed") return false
        if (highlightState === "highlighted") return true
        // 缩放足够大时显示所有标签，否则只显示连接数 ≥3 的
        if (globalScale > LABEL_MIN_SCALE) return true
        return kpNode.val >= 3
      })()

      if (shouldShowLabel) {
        const fontSize = 11 / globalScale
        ctx.font = `${fontSize}px -apple-system, system-ui, sans-serif`
        ctx.textAlign = "left"
        ctx.textBaseline = "middle"
        // 标签背景（Obsidian 风格：浅色背景提升可读性）
        const labelX = (kpNode.x ?? 0) + radius + 2
        const labelY = kpNode.y ?? 0
        const labelWidth = ctx.measureText(kpNode.name).width
        ctx.fillStyle = "rgba(255,255,255,0.85)"
        ctx.fillRect(labelX - 1, labelY - fontSize / 2 - 1, labelWidth + 2, fontSize + 2)
        // 标签文字
        ctx.fillStyle = "rgba(30, 41, 59, 0.95)"
        ctx.fillText(kpNode.name, labelX, labelY)
      }

      ctx.restore()
    },
    [searchText, matchedIds, activeFocusId, activeNeighborIds, viewMode],
  )

  // 边绘制（Obsidian 风格：极细半透明线，颜色区分类型）
  const linkCanvasObject = useCallback(
    (link: LinkObject, ctx: CanvasRenderingContext2D) => {
      const kpLink = link as KpGraphLink
      const source = kpLink.source as KpGraphNode
      const target = kpLink.target as KpGraphNode
      if (!source.x || !source.y || !target.x || !target.y) return

      // 高亮逻辑：有焦点时仅高亮含邻居节点的边
      const isHighlighted =
        activeFocusId === null ||
        activeNeighborIds.has(source.id) ||
        activeNeighborIds.has(target.id)
      const alpha = isHighlighted ? 1 : 0.08

      ctx.save()
      ctx.globalAlpha = alpha
      ctx.strokeStyle = kpLink.edgeType === "prerequisite" ? EDGE_PREREQ_COLOR : EDGE_PARENT_COLOR
      ctx.lineWidth = 0.8
      ctx.beginPath()
      ctx.moveTo(source.x, source.y)
      ctx.lineTo(target.x, target.y)
      ctx.stroke()
      ctx.restore()
    },
    [activeFocusId, activeNeighborIds],
  )

  const handleNodeClick = useCallback(
    (node: NodeObject) => {
      const kpNode = node as KpGraphNode
      onSelectKp(kpNode.id === selectedKpId ? null : kpNode.id)
    },
    [onSelectKp, selectedKpId],
  )

  const handleBackgroundClick = useCallback(() => {
    onSelectKp(null)
  }, [onSelectKp])

  const handleNodeHover = useCallback((node: NodeObject | null) => {
    setHoveredKpId(node ? (node as KpGraphNode).id : null)
    if (containerRef.current) {
      containerRef.current.style.cursor = node ? "pointer" : "default"
    }
  }, [])

  if (data.knowledgePoints.length === 0) {
    return (
      <EmptyState
        icon={Share2}
        title={t("reader.emptyKnowledge")}
        description={t("reader.emptyKnowledgeDesc")}
        className="h-full border-none shadow-none bg-transparent"
      />
    )
  }

  return (
    <div ref={containerRef} className="w-full h-full relative bg-muted/20">
      <ForceGraph2D
        ref={fgRef}
        graphData={graphData}
        width={dimensions.width}
        height={dimensions.height}
        nodeId="id"
        nodeRelSize={1}
        nodeCanvasObject={nodeCanvasObject}
        linkCanvasObject={linkCanvasObject}
        onNodeClick={handleNodeClick}
        onBackgroundClick={handleBackgroundClick}
        onNodeHover={handleNodeHover}
        cooldownTicks={150}
        enableNodeDrag
        enableZoomInteraction
        enablePanInteraction
        minZoom={0.15}
        maxZoom={5}
      />
    </div>
  )
}

export function ForceKnowledgeGraph(props: ForceGraphProps) {
  return <ForceGraphInner {...props} />
}
