<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref, watch, computed } from 'vue'
import { Editor, Extension } from '@tiptap/core'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import Bold from '@tiptap/extension-bold'
import Italic from '@tiptap/extension-italic'
import Underline from '@tiptap/extension-underline'
import Strike from '@tiptap/extension-strike'
import Heading from '@tiptap/extension-heading'
import Code from '@tiptap/extension-code'
import CodeBlock from '@tiptap/extension-code-block'
import BulletList from '@tiptap/extension-bullet-list'
import OrderedList from '@tiptap/extension-ordered-list'
import ListItem from '@tiptap/extension-list-item'
import Blockquote from '@tiptap/extension-blockquote'
import Link from '@tiptap/extension-link'
import HorizontalRule from '@tiptap/extension-horizontal-rule'
import Dropcursor from '@tiptap/extension-dropcursor'
import Gapcursor from '@tiptap/extension-gapcursor'
import History from '@tiptap/extension-history'
import Placeholder from '@tiptap/extension-placeholder'
import TextAlign from '@tiptap/extension-text-align'
import Highlight from '@tiptap/extension-highlight'
import Color from '@tiptap/extension-color'
import TextStyle from '@tiptap/extension-text-style'
import TaskList from '@tiptap/extension-task-list'
import TaskItem from '@tiptap/extension-task-item'
import Table from '@tiptap/extension-table'
import TableRow from '@tiptap/extension-table-row'
import TableCell from '@tiptap/extension-table-cell'
import TableHeader from '@tiptap/extension-table-header'
import CharacterCount from '@tiptap/extension-character-count'
import Subscript from '@tiptap/extension-subscript'
import Superscript from '@tiptap/extension-superscript'
import { uploadFileToJingrow } from '@/shared/api/common'
import ImageResizable from '@/shared/extensions/image-resize'

interface Df {
  fieldname: string
  fieldtype: string
  label?: string
  description?: string
  [key: string]: any
}

const props = defineProps<{
  df: Df
  modelValue: any
  disabled?: boolean
  record?: Record<string, any>
  canEdit?: boolean
  ctx?: any
}>()

// Label布局：上下结构(vertical) 或 左右结构(horizontal)
const labelLayout = computed(() => props.df.label_layout || 'vertical')

const emit = defineEmits<{
  (e: 'update:modelValue', v: any): void
  (e: 'onSave'): void
}>()

const mountEl = ref<HTMLElement | null>(null)
let editor: Editor | null = null
let sourceShowing = false
let sourceTextarea: HTMLTextAreaElement | null = null

// FontSize 扩展：对齐 jingrow 实现，基于 TextStyle，提供 setFontSize/unsetFontSize
// 保留实现以对齐 jingrow；如需启用，加入 extensions
const FontSize = Extension.create({
  name: 'fontSize',
  addGlobalAttributes() {
    return [
      {
        types: ['textStyle'],
        attributes: {
          fontSize: {
            default: null,
            parseHTML: element => {
              const size = (element as HTMLElement).style.fontSize
              return size || null
            },
            renderHTML: attributes => {
              if (!attributes.fontSize) return {}
              return { style: `font-size: ${attributes.fontSize}` }
            },
          },
        },
      },
    ]
  },
})

// 引入谷歌 Material Symbols 字体以获得一致的图标
if (!document.getElementById('material-symbols-font')) {
  const link = document.createElement('link')
  link.id = 'material-symbols-font'
  link.rel = 'stylesheet'
  link.href = 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined'
  document.head.appendChild(link)
}

function icon(name: string) {
  const span = document.createElement('span')
  span.className = 'material-symbols-outlined'
  span.textContent = name
  return span
}

function divider() {
  const el = document.createElement('span')
  el.className = 'tiptap-toolbar-divider'
  return el
}

function button(title: string, iconName: string, onClick: () => void) {
  const btn = document.createElement('button')
  btn.type = 'button'
  btn.className = 'tiptap-toolbar-btn'
  btn.title = title
  btn.appendChild(icon(iconName))
  btn.addEventListener('mousedown', (e) => { e.preventDefault(); onClick() })
  return btn
}

function dropdown(title: string, iconName: string, items: Array<{ label: string; onClick: () => void }>) {
  const wrap = document.createElement('div')
  wrap.className = 'tiptap-toolbar-dropdown'
  const btn = document.createElement('button')
  btn.type = 'button'
  btn.className = 'tiptap-toolbar-btn'
  btn.title = title
  btn.appendChild(icon(iconName))
  const caret = document.createElement('span')
  caret.className = 'tiptap-toolbar-caret'
  caret.textContent = ''
  wrap.appendChild(btn)
  const menu = document.createElement('div')
  menu.className = 'tiptap-toolbar-dropdown-menu'
  items.forEach(it => {
    const item = document.createElement('div')
    item.className = 'tiptap-toolbar-dropdown-item'
    item.textContent = it.label
    item.onmousedown = (e) => { e.preventDefault(); it.onClick(); menu.style.display = 'none' }
    menu.appendChild(item)
  })
  btn.onmousedown = (e) => { e.preventDefault(); menu.style.display = menu.style.display === 'block' ? 'none' : 'block' }
  document.addEventListener('mousedown', (e) => { if (!wrap.contains(e.target as Node)) menu.style.display = 'none' })
  btn.appendChild(caret)
  wrap.appendChild(menu)
  return wrap
}

// 面板关闭
function closeAllPanels() {
  document.querySelectorAll('.tiptap-link-panel, .tiptap-image-panel, .tiptap-color-panel, .tiptap-highlight-panel').forEach(el => el.remove())
}

function showLinkPanel(toolbar: HTMLElement, e: MouseEvent, ed: Editor) {
  closeAllPanels()
  const panel = document.createElement('div')
  panel.className = 'tiptap-link-panel'
  panel.style.position = 'absolute'
  panel.style.zIndex = '20'
  panel.style.background = '#fff'
  panel.style.border = '1px solid #eee'
  panel.style.borderRadius = '8px'
  panel.style.boxShadow = '0 2px 8px rgba(0,0,0,0.08)'
  panel.style.padding = '8px'
  panel.style.display = 'flex'
  panel.style.gap = '6px'
  const input = document.createElement('input')
  input.type = 'text'
  input.placeholder = 'https://...'
  input.style.width = '240px'
  const apply = document.createElement('button')
  apply.textContent = '应用'
  apply.className = 'tiptap-toolbar-btn'
  apply.onmousedown = (ev) => { ev.preventDefault(); const url = input.value.trim(); if (url) ed.chain().focus().setLink({ href: url }).run(); panel.remove() }
  panel.appendChild(input)
  panel.appendChild(apply)
  const btnEl = e.currentTarget as HTMLElement
  const btnRect = btnEl.getBoundingClientRect()
  const barRect = toolbar.getBoundingClientRect()
  panel.style.left = `${btnRect.left - barRect.left}px`
  panel.style.top = `${btnRect.bottom - barRect.top + 4}px`
  toolbar.appendChild(panel)
}

function showImagePanel(toolbar: HTMLElement, e: MouseEvent, ed: Editor) {
  closeAllPanels()
  const panel = document.createElement('div')
  panel.className = 'tiptap-image-panel'
  panel.style.position = 'absolute'
  panel.style.zIndex = '20'
  panel.style.background = '#fff'
  panel.style.border = '1px solid #eee'
  panel.style.borderRadius = '8px'
  panel.style.boxShadow = '0 2px 8px rgba(0,0,0,0.08)'
  panel.style.padding = '8px'
  panel.style.display = 'flex'
  panel.style.gap = '6px'
  panel.style.alignItems = 'center'
  
  // 上传文件按钮
  const uploadBtn = document.createElement('button')
  uploadBtn.innerHTML = '<span class="material-symbols-outlined" style="font-size: 18px;">cloud_upload</span>'
  uploadBtn.className = 'tiptap-toolbar-btn'
  uploadBtn.title = '上传图片'
  uploadBtn.style.padding = '6px'
  
  // 隐藏的文件输入
  const fileInput = document.createElement('input')
  fileInput.type = 'file'
  fileInput.accept = 'image/*'
  fileInput.style.display = 'none'
  
  uploadBtn.onmousedown = (ev) => {
    ev.preventDefault()
    fileInput.click()
  }
  
  fileInput.onchange = async (ev) => {
    const file = (ev.target as HTMLInputElement).files?.[0]
    if (!file) return
    
    try {
      uploadBtn.innerHTML = '<span class="material-symbols-outlined" style="font-size: 18px;">hourglass_empty</span>'
      uploadBtn.disabled = true
      
      const result = await uploadFileToJingrow(file)
      
      if (result.success && result.file_url) {
        ed.chain().focus().insertContent({ type: 'imageResizable', attrs: { src: result.file_url } }).run()
        panel.remove()
        
        // 图片上传成功后自动保存文档
        emit('update:modelValue', ed.getHTML())
        // 触发保存整个记录
        emit('onSave')
      } else {
        alert(result.error || '上传失败')
        uploadBtn.innerHTML = '<span class="material-symbols-outlined" style="font-size: 18px;">cloud_upload</span>'
        uploadBtn.disabled = false
      }
    } catch (error) {
      console.error('Upload error:', error)
      alert('上传失败')
      uploadBtn.innerHTML = '<span class="material-symbols-outlined" style="font-size: 18px;">cloud_upload</span>'
      uploadBtn.disabled = false
    }
  }
  
  // URL输入框
  const input = document.createElement('input')
  input.type = 'text'
  input.placeholder = '图片地址 https://...'
  input.style.width = '200px'
  input.style.flex = '1'
  
  // 插入按钮
  const apply = document.createElement('button')
  apply.textContent = '插入'
  apply.className = 'tiptap-toolbar-btn'
  apply.onmousedown = (ev) => { 
    ev.preventDefault()
    const url = input.value.trim()
    if (url) {
      ed.chain().focus().insertContent({ type: 'imageResizable', attrs: { src: url } }).run()
      panel.remove()
      
      // 图片插入成功后自动保存文档
      emit('update:modelValue', ed.getHTML())
      // 触发保存整个记录
      emit('onSave')
    }
  }
  
  panel.appendChild(uploadBtn)
  panel.appendChild(fileInput)
  panel.appendChild(input)
  panel.appendChild(apply)
  
  const btnEl = e.currentTarget as HTMLElement
  const btnRect = btnEl.getBoundingClientRect()
  const barRect = toolbar.getBoundingClientRect()
  panel.style.left = `${btnRect.left - barRect.left}px`
  panel.style.top = `${btnRect.bottom - barRect.top + 4}px`
  toolbar.appendChild(panel)
}

function showColorPanel(toolbar: HTMLElement, e: MouseEvent, ed: Editor) {
  closeAllPanels()
  const panel = document.createElement('div')
  panel.className = 'tiptap-color-panel'
  panel.style.position = 'absolute'
  panel.style.zIndex = '20'
  panel.style.background = '#fff'
  panel.style.border = '1px solid #eee'
  panel.style.borderRadius = '8px'
  panel.style.boxShadow = '0 2px 8px rgba(0,0,0,0.08)'
  panel.style.padding = '8px'
  const colors = ['#000000','#262626','#595959','#8c8c8c','#bfbfbf','#d9d9d9','#f0f0f0','#ffffff','#f5222d','#fa8c16','#fadb14','#52c41a','#13c2c2','#1890ff','#722ed1','#eb2f96']
  const row = document.createElement('div')
  row.style.display = 'flex'; row.style.flexWrap = 'wrap'; row.style.gap = '4px'
  colors.forEach(c => { const s = document.createElement('div'); s.style.width='20px'; s.style.height='20px'; s.style.border='1px solid #eee'; s.style.borderRadius='4px'; s.style.background=c; s.onmousedown=(ev)=>{ev.preventDefault(); ed.chain().focus().setColor(c).run(); panel.remove()}; row.appendChild(s) })
  panel.appendChild(row)
  const btnEl = e.currentTarget as HTMLElement
  const btnRect = btnEl.getBoundingClientRect()
  const barRect = toolbar.getBoundingClientRect()
  panel.style.left = `${btnRect.left - barRect.left}px`
  panel.style.top = `${btnRect.bottom - barRect.top + 4}px`
  toolbar.appendChild(panel)
}

function showHighlightPanel(toolbar: HTMLElement, e: MouseEvent, ed: Editor) {
  closeAllPanels()
  const panel = document.createElement('div')
  panel.className = 'tiptap-highlight-panel'
  panel.style.position = 'absolute'
  panel.style.zIndex = '20'
  panel.style.background = '#fff'
  panel.style.border = '1px solid #eee'
  panel.style.borderRadius = '8px'
  panel.style.boxShadow = '0 2px 8px rgba(0,0,0,0.08)'
  panel.style.padding = '8px'
  const colors = ['#fffbe6','#ffe7ba','#ffd591','#ffc069','#ffbb96','#ff9c6e','#ff7875','#eaff8f','#b7eb8f','#87e8de','#91d5ff']
  const row = document.createElement('div')
  row.style.display = 'flex'; row.style.flexWrap = 'wrap'; row.style.gap = '4px'
  colors.forEach(c => { const s = document.createElement('div'); s.style.width='20px'; s.style.height='20px'; s.style.border='1px solid #eee'; s.style.borderRadius='4px'; s.style.background=c; s.onmousedown=(ev)=>{ev.preventDefault(); ed.chain().focus().setHighlight({ color: c }).run(); panel.remove()}; row.appendChild(s) })
  panel.appendChild(row)
  const btnEl = e.currentTarget as HTMLElement
  const btnRect = btnEl.getBoundingClientRect()
  const barRect = toolbar.getBoundingClientRect()
  panel.style.left = `${btnRect.left - barRect.left}px`
  panel.style.top = `${btnRect.bottom - barRect.top + 4}px`
  toolbar.appendChild(panel)
}

let panelHandlersBound = false
function ensurePanelGlobalHandlers(_toolbar: HTMLElement) {
  if (panelHandlersBound) return
  panelHandlersBound = true
  document.addEventListener('mousedown', (ev) => {
    const target = ev.target as HTMLElement
    const isInPanel = !!target.closest('.tiptap-link-panel, .tiptap-image-panel, .tiptap-color-panel, .tiptap-highlight-panel')
    const isToolbarBtn = !!target.closest('.tiptap-toolbar-btn')
    if (!isInPanel && !isToolbarBtn) closeAllPanels()
  })
  document.addEventListener('keydown', (ev) => {
    if (ev.key === 'Escape') closeAllPanels()
  })
}

function togglePanel(toolbar: HTMLElement, e: MouseEvent, ed: Editor, type: 'link' | 'image' | 'color' | 'highlight') {
  const existing = document.querySelector(`.tiptap-${type}-panel`)
  if (existing) {
    existing.remove()
    return
  }
  switch (type) {
    case 'link':
      showLinkPanel(toolbar, e, ed)
      break
    case 'image':
      showImagePanel(toolbar, e, ed)
      break
    case 'color':
      showColorPanel(toolbar, e, ed)
      break
    case 'highlight':
      showHighlightPanel(toolbar, e, ed)
      break
  }
}

function buildToolbar(container: HTMLElement, ed: Editor) {
  const bar = document.createElement('div')
  bar.className = 'tiptap-toolbar'

  // 标题下拉：H1-H6 与 正文
  bar.appendChild(dropdown('标题', 'format_paragraph', [
    { label: '标题1', onClick: () => ed.chain().focus().toggleHeading({ level: 1 }).run() },
    { label: '标题2', onClick: () => ed.chain().focus().toggleHeading({ level: 2 }).run() },
    { label: '标题3', onClick: () => ed.chain().focus().toggleHeading({ level: 3 }).run() },
    { label: '标题4', onClick: () => ed.chain().focus().toggleHeading({ level: 4 }).run() },
    { label: '标题5', onClick: () => ed.chain().focus().toggleHeading({ level: 5 }).run() },
    { label: '标题6', onClick: () => ed.chain().focus().toggleHeading({ level: 6 }).run() },
    { label: '正文', onClick: () => ed.chain().focus().setParagraph().run() },
  ]))

  // 字号下拉（与 jingrow 对齐：TextStyle + setMark/unsetMark）
  bar.appendChild(dropdown('字号', 'format_size', [
    ...[8,10,12,14,16,18,20,24,28,32,36,48,64].map(sz => ({
      label: `${sz}px`,
      onClick: () => ed.chain().focus().setMark('textStyle', { fontSize: `${sz}px` }).run(),
    })),
    { label: '重置', onClick: () => ed.chain().focus().unsetMark('textStyle', { extendEmptyMarkRange: true }).run() }
  ]))
  // 分隔符
  bar.appendChild(divider())

  // 表格 & 对齐（顺序与 jingrow 对齐）
  bar.appendChild(dropdown('表格', 'table', [
    { label: '插入表格', onClick: () => ed.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run() },
    { label: '删除表格', onClick: () => ed.chain().focus().deleteTable().run() },
    { label: '插入行(下)', onClick: () => ed.chain().focus().addRowAfter().run() },
    { label: '插入列(右)', onClick: () => ed.chain().focus().addColumnAfter().run() },
    { label: '删除行', onClick: () => ed.chain().focus().deleteRow().run() },
    { label: '删除列', onClick: () => ed.chain().focus().deleteColumn().run() },
  ]))

  bar.appendChild(dropdown('对齐', 'format_align_left', [
    { label: '左对齐', onClick: () => ed.chain().focus().setTextAlign('left').run() },
    { label: '居中', onClick: () => ed.chain().focus().setTextAlign('center').run() },
    { label: '右对齐', onClick: () => ed.chain().focus().setTextAlign('right').run() },
  ]))

  bar.appendChild(divider())

  // 基础样式（含行内代码）
  bar.appendChild(button('加粗', 'format_bold', () => ed.chain().focus().toggleBold().run()))
  bar.appendChild(button('斜体', 'format_italic', () => ed.chain().focus().toggleItalic().run()))
  bar.appendChild(button('下划线', 'format_underlined', () => ed.chain().focus().toggleUnderline().run()))
  bar.appendChild(button('删除线', 'strikethrough_s', () => ed.chain().focus().toggleStrike().run()))
  bar.appendChild(button('行内代码', 'code', () => ed.chain().focus().toggleCode().run()))

  bar.appendChild(divider())

  // 高级格式：代码块/引用/上下标
  bar.appendChild(button('代码块', 'data_object', () => ed.chain().focus().toggleCodeBlock().run()))
  bar.appendChild(button('引用', 'format_quote', () => ed.chain().focus().toggleBlockquote().run()))
  bar.appendChild(button('下标', 'subscript', () => ed.chain().focus().toggleSubscript().run()))
  bar.appendChild(button('上标', 'superscript', () => ed.chain().focus().toggleSuperscript().run()))

  bar.appendChild(divider())

  // 列表
  bar.appendChild(button('无序列表', 'format_list_bulleted', () => ed.chain().focus().toggleBulletList().run()))
  bar.appendChild(button('有序列表', 'format_list_numbered', () => ed.chain().focus().toggleOrderedList().run()))
  bar.appendChild(button('任务列表', 'checklist', () => ed.chain().focus().toggleTaskList().run()))

  bar.appendChild(divider())

  // 链接/图片/分割线
  bar.appendChild(button('链接', 'link', () => {}))
  bar.lastElementChild?.addEventListener('mousedown', (e) => { e.preventDefault(); togglePanel(bar, e as MouseEvent, ed, 'link') })
  bar.appendChild(button('图片', 'image', () => {}))
  bar.lastElementChild?.addEventListener('mousedown', (e) => { e.preventDefault(); togglePanel(bar, e as MouseEvent, ed, 'image') })
  bar.appendChild(button('分割线', 'horizontal_rule', () => ed.chain().focus().setHorizontalRule().run()))

  bar.appendChild(divider())

  // 颜色
  bar.appendChild(button('文字颜色', 'format_color_text', () => {}))
  bar.lastElementChild?.addEventListener('mousedown', (e) => { e.preventDefault(); togglePanel(bar, e as MouseEvent, ed, 'color') })
  bar.appendChild(button('背景色', 'format_color_fill', () => {}))
  bar.lastElementChild?.addEventListener('mousedown', (e) => { e.preventDefault(); togglePanel(bar, e as MouseEvent, ed, 'highlight') })

  bar.appendChild(divider())

  bar.appendChild(button('撤销', 'undo', () => ed.chain().focus().undo().run()))
  bar.appendChild(button('重做', 'redo', () => ed.chain().focus().redo().run()))

  bar.appendChild(divider())

  // 源码模式
  const srcBtn = button('显示源代码', 'code_blocks', () => {
    toggleSourceMode()
  })
  bar.appendChild(srcBtn)

  container.appendChild(bar)
}

function toggleSourceMode() {
  if (!editor) return
  if (!sourceShowing) {
    // 进入源码模式：直接在编辑区域显示文本框
    const html = editor.getHTML()
    const host = (mountEl.value as HTMLElement).querySelector('.jeditor') as HTMLElement | null
    if (host) {
      // 获取编辑器容器的实际高度
      const editorHeight = host.offsetHeight
      
      const ta = document.createElement('textarea')
      ta.className = 'jeditor-source-textarea'
      ta.style.width = '100%'
      ta.style.height = `${editorHeight}px`  // 使用编辑器实际高度
      ta.style.fontFamily = '"SF Mono", "Monaco", "Inconsolata", "Roboto Mono", "Source Code Pro", monospace'
      ta.style.fontSize = '13px'
      ta.style.lineHeight = '1.6'
      ta.style.padding = '16px'
      ta.style.border = 'none'
      ta.style.borderRadius = '8px'
      ta.style.backgroundColor = '#fafbfc'
      ta.style.color = '#24292e'
      ta.style.whiteSpace = 'pre-wrap'
      ta.style.wordWrap = 'break-word'
      ta.style.resize = 'none'  // 禁用拖拽调整大小
      ta.style.outline = 'none'
      ta.style.boxShadow = 'inset 0 1px 3px rgba(0,0,0,0.1)'
      ta.style.transition = 'all 0.2s ease'
      ta.style.boxSizing = 'border-box'  // 确保 padding 包含在高度内
      // 异步格式化HTML
      formatHTML(html).then(formatted => {
        ta.value = formatted
      })
      
      host.style.display = 'none'
      // 插在编辑器后，位置与编辑区域一致
      host.insertAdjacentElement('afterend', ta)
      sourceTextarea = ta
      sourceShowing = true
    }
  } else {
    // 退出源码模式：移除文本框，恢复可视化编辑
    const html = sourceTextarea?.value || ''
    editor!.commands.setContent(html)
    const host = (mountEl.value as HTMLElement).querySelector('.jeditor') as HTMLElement | null
    if (sourceTextarea) {
      sourceTextarea.remove()
      sourceTextarea = null
    }
    if (host) host.style.display = ''
    sourceShowing = false
  }
}

// 使用Prettier格式化HTML，提高可读性（动态导入 + await）
async function formatHTML(html: string): Promise<string> {
  if (!html) return ''
  
  try {
    const prettier = await import('prettier/standalone')
    const parserHtml = await import('prettier/parser-html')
    
    const result = await prettier.format(html, {
      parser: 'html',
      plugins: [parserHtml],
      printWidth: 120,
      tabWidth: 2,
      useTabs: false,
      htmlWhitespaceSensitivity: 'ignore',
      bracketSameLine: false,
      singleAttributePerLine: false
    })
    
    return result
  } catch (error) {
    console.warn('Prettier formatting failed, using simple formatting:', error)
    // 回退到简单格式化
    return formatHTMLSimple(html)
  }
}

// 简单但有效的HTML格式化函数（回退方案）
function formatHTMLSimple(html: string): string {
  if (!html) return ''
  
  // 在标签之间添加换行
  let formatted = html.replace(/></g, '>\n<')
  
  // 分割成行并处理缩进
  const lines = formatted.split('\n')
  let indentLevel = 0
  const indentSize = 2
  
  const result = lines.map(line => {
    const trimmed = line.trim()
    if (!trimmed) return ''
    
    // 闭合标签减少缩进
    if (trimmed.match(/^<\/[^>]+>$/)) {
      indentLevel = Math.max(0, indentLevel - 1)
    }
    
    // 添加缩进
    const indented = ' '.repeat(indentLevel * indentSize) + trimmed
    
    // 开放标签增加缩进（排除自闭合标签）
    if (trimmed.match(/^<[^/][^>]*[^/]>$/) && !trimmed.match(/^<[^>]*\/>$/)) {
      indentLevel++
    }
    
    return indented
  })
  
  return result.join('\n')
}

function mountControl() {
  if (!mountEl.value) return
  mountEl.value.innerHTML = ''

  const toolbarHost = document.createElement('div')
  const editorHost = document.createElement('div')
  editorHost.className = 'jeditor'
  mountEl.value.appendChild(toolbarHost)
  mountEl.value.appendChild(editorHost)

  editor = new Editor({
    element: editorHost,
    content: props.modelValue || '',
    editable: !props.disabled,
    extensions: [
      Document,
      Paragraph,
      Text,
      Bold,
      Italic,
      Underline,
      Strike,
      Heading,
      Code,
      CodeBlock,
      BulletList,
      OrderedList,
      ListItem,
      Blockquote,
      Link.configure({ openOnClick: false }),
      ImageResizable,
      HorizontalRule,
      Dropcursor,
      Gapcursor,
      History,
      Placeholder.configure({ placeholder: props.df?.label || props.df?.fieldname || '请输入内容...' }),
      TextAlign.configure({ types: ['heading', 'paragraph', 'imageResizable'] }),
      Color,
      TextStyle,
      FontSize,
      Highlight.configure({ multicolor: true }),
      TaskList,
      TaskItem,
      Table.configure({ resizable: true }),
      TableRow,
      TableHeader,
      TableCell,
      CharacterCount,
      Subscript,
      Superscript,
    ],
    onUpdate: ({ editor }) => {
      emit('update:modelValue', editor.getHTML())
    },
  })

  buildToolbar(toolbarHost, editor)
  ensurePanelGlobalHandlers(toolbarHost)
}

function unmountControl() {
  try {
    if (editor) editor.destroy()
  } catch {}
  editor = null
}

onMounted(() => {
  mountControl()
})

onBeforeUnmount(() => {
  unmountControl()
})

watch(() => props.modelValue, (val) => {
  if (!editor) return
  const html = val || ''
  if (html !== editor.getHTML()) {
    editor.commands.setContent(html, false)
  }
})

watch(() => props.disabled, (disabled) => {
  if (editor) editor.setEditable(!disabled)
})
</script>

<template>
  <div :class="['field-wrapper', `layout-${labelLayout}`]">
    <label class="field-label">
      {{ ctx?.t ? ctx.t(df.label || df.fieldname) : (df.label || df.fieldname) }}
      <span v-if="df.reqd" class="required">*</span>
    </label>
    <div class="jeditor-control">
      <div ref="mountEl"></div>
    </div>
  </div>
</template>

<style>
.jeditor-control :deep(.tiptap-toolbar) {
  margin-bottom: 8px;
}
.jeditor-control :deep(.jeditor) {
  min-height: 300px;
}
.jeditor-control,
.jeditor-control > div { width: 100%; }

.required {
  color: #ef4444;
  font-weight: bold;
}
.tiptap-toolbar {
    display: flex;
    flex-wrap: wrap;
    background: #fff;
    border-bottom: 1px solid #eee;
    padding: 6px 8px;
    margin-bottom: 4px;
    border-radius: 4px 4px 0 0;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
    align-items: center;
    gap: 2px;
    position: relative;
}
.tiptap-toolbar-btn {
  border: none;
  background: none;
  border-radius: 6px;
  padding: 0 6px;
  height: 32px;
  min-width: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: #444;
  transition: all .15s cubic-bezier(0.2,0,0.2,1);
}
.tiptap-toolbar-btn:hover { background: #f0f2f5; color: #1fc76f; transform: translateY(-1px); box-shadow: 0 2px 4px rgba(0,0,0,0.08); }
.tiptap-toolbar-btn.active { background: rgba(31,199,111,0.1); color: #1fc76f; box-shadow: none; }
.tiptap-toolbar-divider {
  display: inline-block;
  width: 1px;
  height: 20px;
  background: linear-gradient(to bottom, transparent, #e5e6eb, transparent);
  margin: 0 6px;
  align-self: center;
}
.tiptap-toolbar-dropdown {
  position: relative;
  display: inline-block;
}
.tiptap-toolbar-dropdown-menu {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 160px;
  background: #fff;
  border: 1px solid #eee;
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.08);
  z-index: 20;
  padding: 6px 0;
}
.tiptap-toolbar-dropdown-item {
  padding: 6px 12px;
  cursor: pointer;
  white-space: nowrap;
}
.tiptap-toolbar-dropdown-item:hover {
  background: #f5f7fa;
}
.jeditor {
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 12px;
  min-height: 300px;
  width: 100%;
  box-sizing: border-box;
  background: #fff;
  transition: all 0.2s ease;
  position: relative;
}
.jeditor:focus, .jeditor:focus-within, .jeditor[contenteditable="true"]:focus {
    border: 1.5px solid #1fc76f;
    box-shadow: 0 0 0 2px #1fc76f22;
    outline: none;
}
.jeditor[contenteditable="true"],
.jeditor *:focus,
.jeditor *:focus-visible,
.jeditor *:active {
  outline: none;
  border: none;
  box-shadow: none;
}
.tiptap-toolbar-caret { width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 6px solid currentColor; display: inline-block; margin-left: 4px; opacity: .8; }
.tiptap-toolbar .material-symbols-outlined { font-size: 18px !important; line-height: 18px !important; width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center; }
.jeditor-source-textarea { 
  width: 100% !important; 
  box-sizing: border-box; 
  display: block; 
  background: #fafbfc !important;
  color: #24292e !important;
  font-family: "SF Mono", "Monaco", "Inconsolata", "Roboto Mono", "Source Code Pro", monospace !important;
  font-size: 13px !important;
  line-height: 1.6 !important;
  padding: 16px !important;
  border: none !important;
  border-radius: 8px !important;
  outline: none !important;
  box-shadow: inset 0 1px 3px rgba(0,0,0,0.1) !important;
  transition: all 0.2s ease !important;
  resize: vertical !important;
}
.jeditor-source-textarea:focus {
  background: #ffffff !important;
  box-shadow: inset 0 1px 3px rgba(0,0,0,0.1), 0 0 0 3px rgba(59, 130, 246, 0.1) !important;
}
.jeditor pre {
    background: #f6f8fa;
    border-radius: 8px;
    padding: 14px 16px;
    font-family: 'JetBrains Mono', 'Fira Mono', 'Menlo', 'Consolas', 'Liberation Mono', monospace;
    font-size: 15px;
    line-height: 1.7;
    color: #222;
    overflow-x: auto;
    margin: 8px 0;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
    border: 1px solid #e5e6eb;
    position: relative;
}
.jeditor pre code {
    background: none;
    padding: 0;
    border: none;
    font-family: inherit;
    font-size: inherit;
    color: inherit;
}
.jeditor code {
    background: #f6f8fa;
    color: #d6336c;
    font-family: 'JetBrains Mono', 'Fira Mono', 'Menlo', 'Consolas', 'Liberation Mono', monospace;
    font-size: 0.97em;
    border-radius: 4px;
    padding: 2px 6px;
    margin: 0 2px;
    border: none;
    word-break: break-all;
}
.jeditor blockquote {
    border-left: 4px solid #1fc76f;
    background: #f8fefb;
    margin: 8px 0;
    padding: 10px 18px;
    color: #444;
    font-size: 1.05em;
    border-radius: 4px;
    position: relative;
}
.jeditor img {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 8px 0;
    border-radius: 4px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}

/* 图片选中效果样式 */
[data-type="imageResizable"] { 
    box-shadow: 0 0 0 1px #1fc76f33; 
    transition: box-shadow 0.2s; 
    position: relative;
}
[data-type="imageResizable"]:hover,
[data-type="imageResizable"].ProseMirror-selectednode { 
    box-shadow: 0 0 0 2px #1fc76f; 
}
[data-type="imageResizable"]:hover .image-drag-handle,
[data-type="imageResizable"].ProseMirror-selectednode .image-drag-handle {
    background: rgba(31, 199, 111, 0.1);
    border: 1px dashed #1fc76f;
}
[data-type="imageResizable"]:hover .image-resize-handle,
[data-type="imageResizable"].ProseMirror-selectednode .image-resize-handle {
    transform: scale(1.1);
    box-shadow: 0 0 4px #1fc76f;
}
/* 缩放手柄默认隐藏，只在悬浮时显示 */
.image-resize-handle {
    opacity: 0;
    transition: opacity 0.2s ease, transform 0.2s ease;
}
[data-type="imageResizable"]:hover .image-resize-handle,
[data-type="imageResizable"].ProseMirror-selectednode .image-resize-handle {
    opacity: 1;
}
.image-drag-handle {
    transition: all 0.2s ease;
    background: transparent;
    border: 1px dashed transparent;
}
.image-drag-handle:hover {
    background: rgba(31, 199, 111, 0.15) !important;
    border: 1px dashed #1fc76f !important;
}
.image-resize-handle {
    transition: all 0.2s ease;
}
.image-resize-handle:hover {
    transform: scale(1.2) !important;
    box-shadow: 0 0 6px #1fc76f !important;
}

/* 图片对齐样式 */
[data-type="imageResizable"] { 
    max-width: 100%;
    margin: 8px 0;
}
[data-type="imageResizable"][data-text-align="left"] {
    margin-left: 0;
    margin-right: auto;
}
[data-type="imageResizable"][data-text-align="center"] {
    margin-left: auto;
    margin-right: auto;
}
[data-type="imageResizable"][data-text-align="right"] {
    margin-left: auto;
    margin-right: 0;
}

.jeditor .ProseMirror [data-type="imageResizable"] {
    max-width: 100% !important;
    width: auto !important;
}
</style>

