<template>
  <div :class="{ 'fullscreen': isFullscreen }" class="markdown-editor-wrapper">
    <!-- 编辑器工具栏 -->
    <div class="editor-toolbar">
      <div class="toolbar-left">
        <a-space>
          <a-tooltip title="标题">
            <a-button size="small" @click="insertAtCursor('# ')">
              <template #icon><span class="toolbar-icon">H</span></template>
            </a-button>
          </a-tooltip>
          <a-divider type="vertical" />
          <a-tooltip title="加粗">
            <a-button size="small" @click="insertWrap('**', '**')">
              <template #icon><span class="toolbar-icon bold">B</span></template>
            </a-button>
          </a-tooltip>
          <a-tooltip title="斜体">
            <a-button size="small" @click="insertWrap('*', '*')">
              <template #icon><span class="toolbar-icon italic">I</span></template>
            </a-button>
          </a-tooltip>
          <a-tooltip title="删除线">
            <a-button size="small" @click="insertWrap('~~', '~~')">
              <template #icon><span class="toolbar-icon">S</span></template>
            </a-button>
          </a-tooltip>
          <a-divider type="vertical" />
          <a-tooltip title="引用">
            <a-button size="small" @click="insertAtCursor('> ')">
              <template #icon><span class="toolbar-icon">&gt;</span></template>
            </a-button>
          </a-tooltip>
          <a-tooltip title="代码块">
            <a-button size="small" @click="insertCodeBlock">
              <template #icon><span class="toolbar-icon">&lt;/&gt;</span></template>
            </a-button>
          </a-tooltip>
          <a-tooltip title="行内代码">
            <a-button size="small" @click="insertWrap('`', '`')">
              <template #icon><span class="toolbar-icon">`</span></template>
            </a-button>
          </a-tooltip>
          <a-divider type="vertical" />
          <a-tooltip title="链接">
            <a-button size="small" @click="insertLink">
              <template #icon><LinkOutlined /></template>
            </a-button>
          </a-tooltip>
          <a-tooltip title="图片">
            <a-button size="small" @click="handleImageUpload">
              <template #icon><PictureOutlined /></template>
            </a-button>
          </a-tooltip>
          <a-divider type="vertical" />
          <a-tooltip title="有序列表">
            <a-button size="small" @click="insertAtCursor('1. ')">
              <template #icon><UnorderedListOutlined /></template>
            </a-button>
          </a-tooltip>
          <a-tooltip title="无序列表">
            <a-button size="small" @click="insertAtCursor('- ')">
              <template #icon><OrderedListOutlined /></template>
            </a-button>
          </a-tooltip>
          <a-divider type="vertical" />
          <a-tooltip title="水平线">
            <a-button size="small" @click="insertAtCursor('\n---\n')">
              <template #icon><MinusOutlined /></template>
            </a-button>
          </a-tooltip>
        </a-space>
      </div>
      <div class="toolbar-right">
        <a-button size="small" @click="toggleFullscreen">
          <template #icon><FullscreenOutlined v-if="!isFullscreen" /><FullscreenExitOutlined v-else /></template>
        </a-button>
      </div>
    </div>

    <!-- 编辑区域 -->
    <div class="editor-body">
      <div class="editor-pane">
        <textarea
          ref="textareaRef"
          v-model="localValue"
          :placeholder="placeholder"
          :style="{ height: isFullscreen ? '100%' : 'auto' }"
          class="markdown-textarea"
          @input="handleInput"
          @keydown="handleKeydown"
          @scroll="syncScroll"
        />
      </div>
      <div v-if="showPreview" class="preview-pane">
        <div class="preview-label">预览</div>
        <div class="markdown-preview" v-html="renderedHtml"></div>
      </div>
    </div>

    <!-- 隐藏的文件上传 -->
    <a-upload
      ref="uploadRef"
      :before-upload="beforeImageUpload"
      :custom-request="handleCoverUpload"
      :show-upload-list="false"
      accept="image/*"
      style="display: none"
    />

    <!-- 插入链接弹窗 -->
    <a-modal
      v-model:open="showLinkModal"
      :width="400"
      title="插入链接"
      @ok="insertLinkConfirm"
    >
      <a-form :model="linkForm" layout="vertical">
        <a-form-item label="链接文本" required>
          <a-input v-model:value="linkForm.text" placeholder="显示的文本" />
        </a-form-item>
        <a-form-item label="链接地址" required>
          <a-input v-model:value="linkForm.url" placeholder="https://..." />
        </a-form-item>
      </a-form>
    </a-modal>

    <!-- 字数统计 -->
    <div class="editor-footer">
      <span class="word-count">{{ wordCount }} 字</span>
      <span class="char-count">{{ charCount }} 字符</span>
    </div>
  </div>
</template>

<script lang="ts" setup>
import {
  LinkOutlined,
  PictureOutlined,
  UnorderedListOutlined,
  OrderedListOutlined,
  MinusOutlined,
  FullscreenOutlined,
  FullscreenExitOutlined,
} from '@ant-design/icons-vue'
import { marked } from 'marked'
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css'
import { message } from 'ant-design-vue'
import { uploadFile } from '@/api/system/file'

interface Props {
  modelValue: string
  placeholder?: string
  showPreview?: boolean
  minHeight?: string
}

const props = withDefaults(defineProps<Props>(), {
  placeholder: '请输入 Markdown 内容...',
  showPreview: true,
  minHeight: '300px',
})

const emit = defineEmits<{
  'update:modelValue': [value: string]
}>()

// 配置 marked
marked.setOptions({
  highlight: function(code: string, lang: string) {
    if (lang && hljs.getLanguage(lang)) {
      return hljs.highlight(code, { language: lang }).value
    }
    return hljs.highlightAuto(code).value
  },
  breaks: true,
  gfm: true,
})

const textareaRef = ref<HTMLTextAreaElement | null>(null)
const uploadRef = ref<any>(null)
const localValue = ref(props.modelValue)
const isFullscreen = ref(false)
const showLinkModal = ref(false)
const linkForm = reactive({
  text: '',
  url: '',
})

// 监听外部值变化
watch(() => props.modelValue, (newVal) => {
  if (newVal !== localValue.value) {
    localValue.value = newVal
  }
})

// 计算属性
const renderedHtml = computed(() => {
  if (!localValue.value) return '<p class="placeholder-hint">预览区域</p>'
  try {
    return marked.parse(localValue.value) as string
  } catch (e) {
    return '<p class="error-hint">渲染出错</p>'
  }
})

const wordCount = computed(() => {
  const text = localValue.value.trim()
  if (!text) return 0
  return text.split(/\s+/).filter(Boolean).length
})

const charCount = computed(() => localValue.value.length)

// 方法
function handleInput() {
  emit('update:modelValue', localValue.value)
}

function handleKeydown(e: KeyboardEvent) {
  // Tab 键插入空格
  if (e.key === 'Tab') {
    e.preventDefault()
    insertAtCursor('  ')
  }
  // Ctrl/Cmd + B 加粗
  if ((e.ctrlKey || e.metaKey) && e.key === 'b') {
    e.preventDefault()
    insertWrap('**', '**')
  }
  // Ctrl/Cmd + I 斜体
  if ((e.ctrlKey || e.metaKey) && e.key === 'i') {
    e.preventDefault()
    insertWrap('*', '*')
  }
  // Ctrl/Cmd + K 链接
  if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
    e.preventDefault()
    insertLink()
  }
}

function insertAtCursor(text: string) {
  const textarea = textareaRef.value
  if (!textarea) return

  const start = textarea.selectionStart
  const end = textarea.selectionEnd
  const before = localValue.value.substring(0, start)
  const after = localValue.value.substring(end)

  localValue.value = before + text + after
  emit('update:modelValue', localValue.value)

  nextTick(() => {
    textarea.focus()
    textarea.selectionStart = textarea.selectionEnd = start + text.length
  })
}

function insertWrap(before: string, after: string) {
  const textarea = textareaRef.value
  if (!textarea) return

  const start = textarea.selectionStart
  const end = textarea.selectionEnd
  const selectedText = localValue.value.substring(start, end) || '文本'
  const text = before + selectedText + after

  localValue.value = localValue.value.substring(0, start) + text + localValue.value.substring(end)
  emit('update:modelValue', localValue.value)

  nextTick(() => {
    textarea.focus()
    textarea.selectionStart = start + before.length
    textarea.selectionEnd = start + before.length + selectedText.length
  })
}

function insertCodeBlock() {
  const textarea = textareaRef.value
  if (!textarea) return

  const start = textarea.selectionStart
  const end = textarea.selectionEnd
  const selectedText = localValue.value.substring(start, end) || '代码'
  const text = '\n```javascript\n' + selectedText + '\n```\n'

  localValue.value = localValue.value.substring(0, start) + text + localValue.value.substring(end)
  emit('update:modelValue', localValue.value)

  nextTick(() => {
    textarea.focus()
    textarea.selectionStart = start + 14 // 跳过 ```javascript\n
    textarea.selectionEnd = start + 14 + selectedText.length
  })
}

function insertLink() {
  const textarea = textareaRef.value
  if (textarea) {
    const selectedText = localValue.value.substring(textarea.selectionStart, textarea.selectionEnd)
    linkForm.text = selectedText || ''
  }
  showLinkModal.value = true
}

function insertLinkConfirm() {
  if (!linkForm.text.trim()) {
    message.warning('请输入链接文本')
    return
  }
  if (!linkForm.url.trim()) {
    message.warning('请输入链接地址')
    return
  }
  insertAtCursor(`[${linkForm.text}](${linkForm.url})`)
  showLinkModal.value = false
  linkForm.text = ''
  linkForm.url = ''
}

function handleImageUpload() {
  const input = document.createElement('input')
  input.type = 'file'
  input.accept = 'image/*'
  input.onchange = async (e) => {
    const file = (e.target as HTMLInputElement).files?.[0]
    if (file) {
      await uploadAndInsertImage(file)
    }
  }
  input.click()
}

function beforeImageUpload(file: File) {
  if (!file.type.startsWith('image/')) {
    message.error('只能上传图片文件')
    return false
  }
  if (file.size > 5 * 1024 * 1024) {
    message.error('图片大小不能超过 5MB')
    return false
  }
  return true
}

async function uploadAndInsertImage(file: File) {
  try {
    message.loading({ content: '上传图片中...', key: 'upload' })
    const record = await uploadFile(file)
    const url = (record?.url || record?.downloadUrl || '').trim()
    if (!url) throw new Error('上传成功但未返回图片地址')
    insertAtCursor(`![${file.name}](${url})`)
    message.success({ content: '图片上传成功', key: 'upload' })
  } catch (e: any) {
    message.error({ content: e?.message || '图片上传失败', key: 'upload' })
  }
}

async function handleCoverUpload(option: any) {
  await uploadAndInsertImage(option.file)
  option.onSuccess?.()
}

function syncScroll(e: Event) {
  const textarea = e.target as HTMLTextAreaElement
  const preview = textarea.parentElement?.nextElementSibling?.querySelector('.markdown-preview') as HTMLElement
  if (preview) {
    const ratio = textarea.scrollTop / (textarea.scrollHeight - textarea.clientHeight)
    preview.scrollTop = ratio * (preview.scrollHeight - preview.clientHeight)
  }
}

function toggleFullscreen() {
  isFullscreen.value = !isFullscreen.value
}

// 暴露方法
defineExpose({
  focus: () => textareaRef.value?.focus(),
  insertText: insertAtCursor,
})
</script>

<style scoped>
.markdown-editor-wrapper {
  border: 1px solid #d9d9d9;
  border-radius: 8px;
  overflow: hidden;
  background: #fff;
  display: flex;
  flex-direction: column;
}

.markdown-editor-wrapper.fullscreen {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1000;
  border-radius: 0;
}

.editor-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 12px;
  background: #fafafa;
  border-bottom: 1px solid #f0f0f0;
}

.toolbar-left {
  flex: 1;
}

.toolbar-right {
  margin-left: 12px;
}

.toolbar-icon {
  font-family: Georgia, serif;
  font-weight: 700;
  font-size: 12px;
}

.toolbar-icon.bold {
  font-weight: 900;
}

.toolbar-icon.italic {
  font-style: italic;
}

.editor-body {
  display: flex;
  flex: 1;
  min-height: v-bind(minHeight);
  overflow: hidden;
}

.editor-pane {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.markdown-textarea {
  flex: 1;
  padding: 16px;
  border: none;
  outline: none;
  resize: none;
  font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
  font-size: 14px;
  line-height: 1.7;
  color: #24292e;
  background: #fff;
}

.markdown-textarea::placeholder {
  color: #aaa;
}

.preview-pane {
  flex: 1;
  border-left: 1px solid #f0f0f0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.preview-label {
  padding: 8px 16px;
  font-size: 12px;
  color: #999;
  background: #fafafa;
  border-bottom: 1px solid #f0f0f0;
}

.markdown-preview {
  flex: 1;
  padding: 16px;
  overflow-y: auto;
  font-size: 14px;
  line-height: 1.8;
  color: #24292e;
}

.markdown-preview :deep(.placeholder-hint),
.markdown-preview :deep(.error-hint) {
  color: #aaa;
  font-style: italic;
}

.markdown-preview :deep(h1) {
  font-size: 1.8em;
  font-weight: 700;
  margin: 0.5em 0;
  padding-bottom: 0.3em;
  border-bottom: 1px solid #eaecef;
}

.markdown-preview :deep(h2) {
  font-size: 1.5em;
  font-weight: 600;
  margin: 0.8em 0 0.4em;
  padding-bottom: 0.25em;
  border-bottom: 1px solid #eaecef;
}

.markdown-preview :deep(h3) {
  font-size: 1.25em;
  font-weight: 600;
  margin: 0.6em 0 0.3em;
}

.markdown-preview :deep(p) {
  margin: 0.8em 0;
}

.markdown-preview :deep(code) {
  padding: 0.2em 0.4em;
  background: #f6f8fa;
  border-radius: 4px;
  font-family: 'Monaco', 'Menlo', monospace;
  font-size: 0.9em;
}

.markdown-preview :deep(pre) {
  padding: 16px;
  background: #f6f8fa;
  border-radius: 8px;
  overflow-x: auto;
}

.markdown-preview :deep(pre code) {
  padding: 0;
  background: transparent;
}

.markdown-preview :deep(blockquote) {
  margin: 1em 0;
  padding: 0.5em 1em;
  border-left: 4px solid #dfe2e5;
  color: #6a737d;
  background: #f6f8fa;
}

.markdown-preview :deep(ul),
.markdown-preview :deep(ol) {
  padding-left: 2em;
  margin: 0.8em 0;
}

.markdown-preview :deep(li) {
  margin: 0.3em 0;
}

.markdown-preview :deep(a) {
  color: #0366d6;
  text-decoration: none;
}

.markdown-preview :deep(a:hover) {
  text-decoration: underline;
}

.markdown-preview :deep(img) {
  max-width: 100%;
  border-radius: 8px;
}

.markdown-preview :deep(hr) {
  margin: 1.5em 0;
  border: none;
  border-top: 1px solid #dfe2e5;
}

.markdown-preview :deep(table) {
  width: 100%;
  border-collapse: collapse;
  margin: 1em 0;
}

.markdown-preview :deep(th),
.markdown-preview :deep(td) {
  padding: 8px 12px;
  border: 1px solid #dfe2e5;
}

.markdown-preview :deep(th) {
  background: #f6f8fa;
  font-weight: 600;
}

.editor-footer {
  display: flex;
  gap: 16px;
  padding: 6px 12px;
  font-size: 12px;
  color: #999;
  background: #fafafa;
  border-top: 1px solid #f0f0f0;
}
</style>
