<template>
  <ClientOnly>
    <div class="re-container">
      <div class="re-toolbar">
        <button
          v-for="btn in leftButtons"
          :key="btn.key"
          :title="btn.tip"
          class="re-btn"
          :class="{ active: btn.isActive?.() }"
          @click="btn.action"
        >
          {{ btn.text }}
        </button>
        <div class="re-split" />
        <button
          v-for="btn in midButtons"
          :key="btn.key"
          :title="btn.tip"
          class="re-btn"
          :class="{ active: btn.isActive?.() }"
          @click="btn.action"
        >
          {{ btn.text }}
        </button>
        <div class="re-split" />
        <button
          v-for="btn in rightButtons"
          :key="btn.key"
          :title="btn.tip"
          class="re-btn"
          :class="{ active: btn.isActive?.() }"
          @click="btn.action"
        >
          {{ btn.text }}
        </button>
        <div class="re-split" />
        <div class="re-group">
          <span class="re-label">字色</span>
          <button
            v-for="c in textColors"
            :key="'tc-' + c"
            class="re-color"
            :style="{ background: c }"
            @click="setTextColor(c)"
            :title="c"
          />
          <button class="re-btn" @click="setTextColor(null)">清除</button>
        </div>
        <div class="re-group">
          <span class="re-label">高亮</span>
          <button
            v-for="c in hlColors"
            :key="'hl-' + c"
            class="re-color"
            :style="{ background: c }"
            @click="setHighlight(c)"
            :title="c"
          />
          <button class="re-btn" @click="setHighlight(null)">清除</button>
        </div>
        <div class="re-split" />
        <div class="re-group">
          <span class="re-label">图片</span>
          <button class="re-btn" @click="imgSetWidth('25%')">25%</button>
          <button class="re-btn" @click="imgSetWidth('50%')">50%</button>
          <button class="re-btn" @click="imgSetWidth('75%')">75%</button>
          <button class="re-btn" @click="imgSetWidth('100%')">100%</button>
          <div class="re-px">
            <input
              type="number"
              min="40"
              max="4096"
              placeholder="px"
              @keydown.stop
              @change="e => imgSetWidth(e.target.value ? `${e.target.value}px` : null)"
            />
            <span>px</span>
          </div>
          <div class="re-range" title="10% - 100%">
            <input type="range" min="10" max="100" step="1" @input="e => imgSetWidth(`${e.target.value}%`)" />
          </div>
          <button class="re-btn" @click="imgSetWidth(null)">自适应</button>
        </div>
        <div class="flex-1" />
        <label class="re-upload">
          <input type="file" accept="image/*" @change="onPickImage" hidden />
          插入图片
        </label>
      </div>
      <div class="re-editor" :class="{ 're-readonly': readonly }">
        <EditorContent :editor="editor" />
      </div>
    </div>
  </ClientOnly>
</template>

<script setup>
import { onBeforeUnmount, onMounted, watch } from 'vue'
import { Editor, EditorContent } from '@tiptap/vue-3'
import { StarterKit } from '@tiptap/starter-kit'
import { Underline } from '@tiptap/extension-underline'
import { Link } from '@tiptap/extension-link'
import { Placeholder } from '@tiptap/extension-placeholder'
import { TextAlign } from '@tiptap/extension-text-align'
import { TextStyle } from '@tiptap/extension-text-style'
import { Color } from '@tiptap/extension-color'
import { Highlight } from '@tiptap/extension-highlight'
import { Table } from '@tiptap/extension-table'
import { TableRow } from '@tiptap/extension-table-row'
import { TableHeader } from '@tiptap/extension-table-header'
import { TableCell } from '@tiptap/extension-table-cell'
import { Image } from '@tiptap/extension-image'
import { useUpload } from '@/composables/useUpload'

const props = defineProps({
  modelValue: { type: String, default: '' },
  placeholder: { type: String, default: '请输入正文，支持粘贴图片、截图、链接等…' },
  readonly: { type: Boolean, default: false },
})
const emit = defineEmits(['update:modelValue', 'change'])

const { uploadImage } = useUpload()
let editor = null

onMounted(() => {
  editor = new Editor({
    content: props.modelValue || '',
    editable: !props.readonly,
    extensions: [
      StarterKit.configure({
        link: false,
        underline: false,
        bulletList: { keepMarks: true },
        orderedList: { keepMarks: true },
      }),
      Underline,
      Highlight,
      Link.configure({
        autolink: true,
        openOnClick: true,
        linkOnPaste: true,
        HTMLAttributes: { rel: 'nofollow', target: '_blank' },
      }),
      Image.configure({ allowBase64: true }),
      Placeholder.configure({ placeholder: props.placeholder }),
      TextStyle,
      Color,
      TextAlign.configure({ types: ['heading', 'paragraph'] }),
      Table.configure({ resizable: true }),
      TableRow,
      TableHeader,
      TableCell,
    ],
    onUpdate: ({ editor }) => {
      const html = editor.getHTML()
      emit('update:modelValue', html)
      emit('change', html)
    },
    editorProps: {
      handlePaste(view, event) {
        const items = event.clipboardData?.items || []
        for (const it of items) {
          if (it.type?.startsWith('image/')) {
            const file = it.getAsFile()
            if (file) {
              event.preventDefault()
              insertImageFile(file)
              return true
            }
          }
        }
        return false
      },
      handleDrop(view, event) {
        const file = event.dataTransfer?.files?.[0]
        if (file && file.type.startsWith('image/')) {
          event.preventDefault()
          insertImageFile(file)
          return true
        }
        return false
      },
    },
  })
})

watch(
  () => props.readonly,
  value => editor?.setEditable(!value),
)

onBeforeUnmount(() => editor?.destroy())

watch(
  () => props.modelValue,
  (val) => {
    if (!editor) return
    const current = editor.getHTML()
    if (val != null && val !== current) {
      editor.commands.setContent(val, false)
    }
  },
)

async function insertImageFile(file) {
  try {
    const url = await uploadImage(file)
    editor.chain().focus().setImage({ src: url, alt: file.name }).run()
  } catch (e) {
    console.warn('[RichEditor] image insert failed:', e)
  }
}

const textColors = ['#111827', '#1f2937', '#374151', '#4b5563', '#6b7280', '#ef4444', '#f97316', '#f59e0b', '#10b981', '#3b82f6', '#6366f1', '#8b5cf6', '#ec4899']
const hlColors = ['#fff7ed', '#fef3c7', '#fef9c3', '#e0f2fe', '#dcfce7', '#e9d5ff', '#ffe4e6']

function setTextColor(c) {
  if (!c) return editor?.chain().focus().unsetColor().run()
  editor?.chain().focus().setColor(c).run()
}
function setHighlight(c) {
  if (!c) return editor?.chain().focus().unsetHighlight().run()
  editor?.chain().focus().toggleHighlight({ color: c }).run()
}

function imgSetWidth(width) {
  const isImageSel = editor?.isActive('image') || editor?.state?.selection?.node?.type?.name === 'image'
  if (!isImageSel) return
  editor?.chain().focus().updateAttributes('image', { width: width || null }).run()
}

const leftButtons = [
  { key: 'undo', text: '↶', tip: '撤销', action: () => editor?.chain().focus().undo().run() },
  { key: 'redo', text: '↷', tip: '重做', action: () => editor?.chain().focus().redo().run() },
  { key: 'hr', text: '—', tip: '分割线', action: () => editor?.chain().focus().setHorizontalRule().run() },
]
const midButtons = [
  { key: 'bold', text: 'B', tip: '加粗', action: () => editor?.chain().focus().toggleBold().run(), isActive: () => editor?.isActive('bold') },
  { key: 'italic', text: 'I', tip: '斜体', action: () => editor?.chain().focus().toggleItalic().run(), isActive: () => editor?.isActive('italic') },
  { key: 'underline', text: 'U', tip: '下划线', action: () => editor?.chain().focus().toggleUnderline().run(), isActive: () => editor?.isActive('underline') },
  { key: 'strike', text: 'S', tip: '删除线', action: () => editor?.chain().focus().toggleStrike().run(), isActive: () => editor?.isActive('strike') },
  { key: 'highlight', text: 'HL', tip: '高亮', action: () => editor?.chain().focus().toggleHighlight().run(), isActive: () => editor?.isActive('highlight') },
  { key: 'h2', text: 'H2', tip: '标题 2', action: () => editor?.chain().focus().toggleHeading({ level: 2 }).run(), isActive: () => editor?.isActive('heading', { level: 2 }) },
  { key: 'h3', text: 'H3', tip: '标题 3', action: () => editor?.chain().focus().toggleHeading({ level: 3 }).run(), isActive: () => editor?.isActive('heading', { level: 3 }) },
  { key: 'ul', text: '• 列表', tip: '无序列表', action: () => editor?.chain().focus().toggleBulletList().run(), isActive: () => editor?.isActive('bulletList') },
  { key: 'ol', text: '1. 列表', tip: '有序列表', action: () => editor?.chain().focus().toggleOrderedList().run(), isActive: () => editor?.isActive('orderedList') },
  { key: 'quote', text: '❝ ❞', tip: '引用', action: () => editor?.chain().focus().toggleBlockquote().run(), isActive: () => editor?.isActive('blockquote') },
  { key: 'code', text: '</>', tip: '代码块', action: () => editor?.chain().focus().toggleCodeBlock().run(), isActive: () => editor?.isActive('codeBlock') },
]
const rightButtons = [
  { key: 'left', text: '⟸', tip: '左对齐', action: () => editor?.chain().focus().setTextAlign('left').run(), isActive: () => editor?.isActive({ textAlign: 'left' }) },
  { key: 'center', text: '⇔', tip: '居中', action: () => editor?.chain().focus().setTextAlign('center').run(), isActive: () => editor?.isActive({ textAlign: 'center' }) },
  { key: 'right', text: '⟹', tip: '右对齐', action: () => editor?.chain().focus().setTextAlign('right').run(), isActive: () => editor?.isActive({ textAlign: 'right' }) },
  { key: 'justify', text: '≋', tip: '两端对齐', action: () => editor?.chain().focus().setTextAlign('justify').run(), isActive: () => editor?.isActive({ textAlign: 'justify' }) },
]

function onPickImage(e) {
  const file = e.target.files?.[0]
  if (file) insertImageFile(file)
  e.target.value = ''
}
</script>

<style scoped>
.re-container {
  border: 1px solid #e5e7eb;
  border-radius: 12px;
  background: #fff;
}
.re-toolbar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  border-bottom: 1px solid #eef1f5;
  flex-wrap: wrap;
}
.re-btn {
  font-size: 12px;
  padding: 6px 10px;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  background: #f9fafb;
}
.re-btn.active {
  background: #eef2ff;
  border-color: #c7d2fe;
}
.re-split {
  width: 1px;
  height: 20px;
  background: #e5e7eb;
  margin: 0 6px;
}
.re-group {
  display: flex;
  align-items: center;
  gap: 6px;
}
.re-label {
  font-size: 12px;
  color: #64748b;
  margin-right: 2px;
}
.re-color {
  width: 18px;
  height: 18px;
  border-radius: 6px;
  border: 1px solid #e5e7eb;
}
.re-upload {
  font-size: 12px;
  padding: 6px 10px;
  border: 1px dashed #d1d5db;
  border-radius: 8px;
  cursor: pointer;
  color: #374151;
  background: #fafafa;
}
.re-editor {
  padding: 14px;
}
.re-readonly {
  background: #f9fafb;
}
.re-editor :deep(.ProseMirror) {
  min-height: 360px;
  outline: none;
  line-height: 1.75;
  font-size: 14px;
}
.re-editor :deep(h2) {
  font-size: 1.25rem;
  margin: 1em 0 0.5em;
}
.re-editor :deep(h3) {
  font-size: 1.125rem;
  margin: 0.9em 0 0.4em;
}
.re-editor :deep(blockquote) {
  border-left: 3px solid #a5b4fc;
  padding: 0.25rem 0.75rem;
  color: #4b5563;
  background: #f9fafb;
}
.re-editor :deep(pre) {
  background: #0f172a;
  color: #e5e7eb;
  padding: 0.75rem;
  border-radius: 8px;
  overflow: auto;
}
.re-editor :deep(table) {
  width: 100%;
  border-collapse: collapse;
  margin: 0.5rem 0;
}
.re-editor :deep(th),
.re-editor :deep(td) {
  border: 1px solid #e5e7eb;
  padding: 6px 8px;
}
.re-editor :deep(img) {
  max-width: 100%;
  height: auto;
  border-radius: 10px;
  box-shadow: 0 6px 20px rgba(2, 6, 23, 0.08);
}
.re-px {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 2px 6px;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  background: #fff;
}
.re-px input {
  width: 72px;
  border: none;
  outline: none;
  padding: 4px 2px;
  background: transparent;
}
.re-range {
  padding: 0 6px;
}
.re-range input {
  height: 22px;
}
</style>
