"use client"

import { useEffect, useState } from "react"
import type { Editor, JSONContent } from "@tiptap/react"
import { useTranslations } from "next-intl"
import {
  Box,
  Brackets,
  CircleSlash,
  FileText,
  Heading,
  Layers,
  Underline,
} from "lucide-react"

import { cn } from "@/shared/lib/utils"
import { Button } from "@/shared/components/ui/button"
import type { QuestionBlockType } from "./extensions/question-block"

interface SelectionToolbarProps {
  editor: Editor | null
  /** 当前选区所在题目块类型(用于显示状态) */
  activeQuestionType?: QuestionBlockType
  className?: string
}

interface ToolbarButtonProps {
  onClick: () => void
  icon: React.ElementType
  label: string
  active?: boolean
}

function ToolbarButton({ onClick, icon: Icon, label, active }: ToolbarButtonProps) {
  return (
    <Button
      type="button"
      variant={active ? "secondary" : "ghost"}
      size="sm"
      className={cn(
        "h-7 gap-1 px-2 text-xs",
        active && "bg-primary/15 text-primary hover:bg-primary/20"
      )}
      onMouseDown={(e) => {
        // 防止点击按钮时编辑器失焦
        e.preventDefault()
      }}
      onClick={(e) => {
        e.preventDefault()
        onClick()
      }}
      title={label}
    >
      <Icon className="h-3.5 w-3.5" />
      <span>{label}</span>
    </Button>
  )
}

/**
 * 把 orderedList/bulletList 转为普通段落,去掉列表标号。
 * 用于把选中文本转为题目块时,避免列表被错误解析为选项(填空/简答题场景)。
 * 列表的每个 listItem 的子块(通常是 paragraph)会被提取为独立段落。
 */
const flattenListToParagraphs = (nodes: JSONContent[]): JSONContent[] => {
  const result: JSONContent[] = []
  for (const node of nodes) {
    if (node.type === "orderedList" || node.type === "bulletList") {
      if (Array.isArray(node.content)) {
        for (const item of node.content) {
          if (Array.isArray(item.content)) {
            result.push(...item.content)
          }
        }
      }
    } else {
      result.push(node)
    }
  }
  // 确保非空(questionBlock 要求 content: "block+")
  if (result.length === 0) {
    result.push({ type: "paragraph", content: [{ type: "text", text: " " }] })
  }
  return result
}

/**
 * 选区标记浮层 —— 选中文本时浮现,提供"标记为题目/分组/加点字/填空"等快捷操作。
 * 跟随选区位置定位,使用 getBoundingClientRect 计算坐标。
 */
export function SelectionToolbar({
  editor,
  activeQuestionType,
  className,
}: SelectionToolbarProps) {
  const t = useTranslations("examHomework")
  const [coords, setCoords] = useState<{ top: number; left: number } | null>(null)
  const [hasSelection, setHasSelection] = useState(false)

  useEffect(() => {
    if (!editor) return

    const updatePosition = () => {
      const { from, to, empty } = editor.state.selection
      if (empty || from === to) {
        setCoords(null)
        setHasSelection(false)
        return
      }

      // 检查选区是否在 questionBlock/groupBlock 内部
      const $from = editor.state.selection.$from
      const depth = $from.depth
      let inBlock = false
      for (let d = depth; d > 0; d--) {
        const node = $from.node(d)
        if (node.type.name === "questionBlock" || node.type.name === "groupBlock") {
          inBlock = true
          break
        }
      }

      // 即使不在块内也允许显示(可标记为题目/分组)
      void inBlock

      // 使用浏览器原生选区的可见矩形(相对视口),避免长文本选区起点
      // 在视口外时 coordsAtPos 返回负坐标导致工具栏跑到页面顶部
      const domSelection = window.getSelection()
      let top: number
      let left: number

      if (domSelection && domSelection.rangeCount > 0) {
        const rect = domSelection.getRangeAt(0).getBoundingClientRect()
        // 选区不可见(滚动出视口)时 rect 为空矩形,此时不显示工具栏
        if (rect.width === 0 && rect.height === 0) {
          setCoords(null)
          setHasSelection(false)
          return
        }
        top = rect.top - 44 // 浮在选区上方
        left = rect.left + rect.width / 2
      } else {
        // 降级:使用 ProseMirror 的 coordsAtPos
        const view = editor.view
        const start = view.coordsAtPos(from)
        const end = view.coordsAtPos(to)
        top = Math.min(start.top, end.top) - 44
        left = (start.left + end.right) / 2
      }

      // 限制在视口内,避免超出顶部或左右边界
      const clampedTop = Math.max(8, Math.min(top, window.innerHeight - 60))
      const clampedLeft = Math.max(120, Math.min(left, window.innerWidth - 120))

      setCoords({ top: clampedTop, left: clampedLeft })
      setHasSelection(true)
    }

    editor.on("selectionUpdate", updatePosition)

    // 鼠标释放后立即更新位置(选区拖动结束时)
    const handlePointerUp = () => {
      // 微延迟确保选区已更新
      requestAnimationFrame(updatePosition)
    }
    document.addEventListener("pointerup", handlePointerUp)

    editor.on("blur", () => {
      // 延迟以允许点击工具栏按钮
      setTimeout(() => {
        if (!editor.isFocused) {
          setCoords(null)
          setHasSelection(false)
        }
      }, 200)
    })

    return () => {
      editor.off("selectionUpdate", updatePosition)
      document.removeEventListener("pointerup", handlePointerUp)
    }
  }, [editor])

  if (!editor || !hasSelection || !coords) return null

  const { from, to, empty } = editor.state.selection
  const hasTextSelection = !empty && from !== to

  const insertQuestion = (type: QuestionBlockType) => {
    const score = type === "composite" ? 5 : 2
    if (!hasTextSelection) {
      // 未选中文本时:插入空题目块
      editor.chain().focus().insertQuestion({ type, score }).run()
      return
    }

    const isChoice =
      type === "single_choice" ||
      type === "multiple_choice" ||
      type === "judgment"

    // 选择题:先尝试 wrapIn(保留列表结构,以便解析为 A/B/C 选项)
    if (isChoice && editor.commands.wrapInQuestion({ type, score })) {
      return
    }

    // 非选择题,或 wrapIn 失败(如在 isolating 节点内):
    // 用 slice 获取选区完整节点结构,把列表转为普通段落(去掉有序序列标号),
    // 然后插入新的 questionBlock。避免列表导致填空/简答题在预览中不显示。
    const { from, to } = editor.state.selection
    const slice = editor.state.doc.slice(from, to)
    const sliceContent = slice.content.toJSON
      ? (slice.content.toJSON() as JSONContent[])
      : Array.isArray(slice.content.content)
        ? slice.content.content.map((n) => n.toJSON() as JSONContent)
        : []

    const content = isChoice
      ? sliceContent.length > 0
        ? sliceContent
        : [{ type: "paragraph", content: [{ type: "text", text: " " }] }]
      : flattenListToParagraphs(sliceContent)

    editor.chain()
      .focus()
      .deleteSelection()
      .insertContent({
        type: "questionBlock",
        attrs: { type, score, questionId: "" },
        content,
      })
      .run()
  }

  /** 通用降级包裹:wrapIn 失败时,把选中文本转为指定节点 */
  const wrapOrInsert = (
    wrapFn: () => boolean,
    insertFn: () => void,
    nodeType: "groupBlock" | "sectionBlock",
    attrs: Record<string, unknown>,
    defaultTitle: string
  ) => {
    if (!hasTextSelection) {
      insertFn()
      return
    }
    const wrapped = wrapFn()
    if (!wrapped) {
      // wrapIn 失败:获取选中文本,删除选区,插入包含选中文本的节点
      const { from, to } = editor.state.selection
      const selectedText = editor.state.doc.textBetween(from, to, "\n")
      const lines = selectedText.split("\n").filter((l) => l.trim().length > 0)
      const content = lines.length > 0
        ? lines.map((line) => ({
            type: "paragraph",
            content: [{ type: "text", text: line }],
          }))
        : [{ type: "paragraph", content: [{ type: "text", text: " " }] }]

      editor.chain()
        .focus()
        .deleteSelection()
        .insertContent({
          type: nodeType,
          attrs: { ...attrs, title: defaultTitle },
          content,
        })
        .run()
    }
  }

  const insertGroup = () => {
    const defaultTitle = t("exam.selectionToolbar.defaultGroupTitle")
    wrapOrInsert(
      () => editor.commands.wrapInGroup(defaultTitle, ""),
      () => editor.chain().focus().insertGroup(defaultTitle, "").run(),
      "groupBlock",
      { instruction: "" },
      defaultTitle
    )
  }

  const insertSection = () => {
    const defaultTitle = t("exam.selectionToolbar.defaultSectionTitle")
    wrapOrInsert(
      () => editor.commands.wrapInSection(defaultTitle, 1),
      () => editor.chain().focus().insertSection(defaultTitle, 1).run(),
      "sectionBlock",
      { level: 1 },
      defaultTitle
    )
  }

  const toggleDotted = () => {
    editor.chain().focus().toggleDotted().run()
  }

  const insertBlank = () => {
    editor.chain().focus().insertBlank().run()
  }

  const insertImage = () => {
    // 触发隐藏的文件输入
    const input = document.createElement("input")
    input.type = "file"
    input.accept = "image/*"
    input.onchange = async () => {
      const file = input.files?.[0]
      if (!file) return
      const formData = new FormData()
      formData.append("file", file)
      formData.append("targetType", "exam")
      try {
        const res = await fetch("/api/upload", { method: "POST", body: formData })
        const data = await res.json()
        if (data?.success && data?.url) {
          editor.chain().focus().setImage({ src: data.url, fileId: data.id }).run()
        }
      } catch (e) {
        console.error("[SelectionToolbar] upload failed", e)
      }
    }
    input.click()
  }

  return (
    <div
      role="toolbar"
      aria-label={t("exam.selectionToolbar.ariaLabel")}
      className={cn(
        "fixed z-50 flex items-center gap-0.5 rounded-md border bg-popover/95 p-1 shadow-md backdrop-blur",
        className
      )}
      style={{
        top: `${Math.max(8, coords.top)}px`,
        left: `${coords.left}px`,
        transform: "translateX(-50%)",
      }}
    >
      <ToolbarButton onClick={insertSection} icon={Layers} label={t("exam.selectionToolbar.sectionLabel")} />
      <ToolbarButton onClick={insertGroup} icon={Heading} label={t("exam.selectionToolbar.groupLabel")} />
      <ToolbarButton
        onClick={() => insertQuestion("single_choice")}
        icon={CircleSlash}
        label={t("exam.selectionToolbar.singleChoice")}
        active={activeQuestionType === "single_choice"}
      />
      <ToolbarButton
        onClick={() => insertQuestion("text")}
        icon={FileText}
        label={t("exam.selectionToolbar.blankShortAnswer")}
        active={activeQuestionType === "text"}
      />
      <ToolbarButton
        onClick={() => insertQuestion("composite")}
        icon={Brackets}
        label={t("exam.selectionToolbar.composite")}
        active={activeQuestionType === "composite"}
      />
      <div className="mx-1 h-5 w-px bg-border" />
      <ToolbarButton onClick={toggleDotted} icon={Underline} label={t("exam.richEditor.dotted")} />
      <ToolbarButton onClick={insertBlank} icon={Box} label={t("exam.richEditor.markBlank")} />
      <ToolbarButton onClick={insertImage} icon={FileText} label={t("exam.selectionToolbar.image")} />
    </div>
  )
}
