<template>
  <div class="border border-solid border-gray-100 h-full">
    <Toolbar :editor="editorRef" :default-config="toolbarConfig" mode="default" />
    <Editor v-model="valueHtml" class="overflow-y-hidden mt-0.5" :style="style" :default-config="editorConfig"
      mode="default" @onCreated="handleCreated" @onChange="change" @customPaste="customPaste" />
  </div>
</template>

<script setup>

import '@wangeditor/editor/dist/css/style.css' // 引入 css

const basePath = import.meta.env.VITE_BASE_API

import { onBeforeUnmount, ref, shallowRef, watch } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { ElMessage } from 'element-plus'
import { getUrl } from '@/utils/image'

const emits = defineEmits(['change', 'update:modelValue'])
const change = (editor) => {
  emits('change', editor)
  emits('update:modelValue', valueHtml.value)
}

const props = defineProps({
  mediaCategory: {
    type: String,
    default: 'rich_edit'
  },
  modelValue: {
    type: String,
    default: ''
  },
  style: {
    type: Object,
    default: () => ({
      height: '20rem',
    })
  },
})

const editorRef = shallowRef()
const valueHtml = ref('')

const toolbarConfig = {}
const editorConfig = {
  placeholder: '请输入内容...',
  MENU_CONF: {}
}
editorConfig.MENU_CONF['uploadImage'] = {
  fieldName: 'file',
  server: basePath + '/cms/mediaFile/upload?category=' + props.mediaCategory,
  customInsert(res, insertFn) {
    if (res.code === 0) {
      const urlPath = getUrl(res.data.mediaFile.url)
      insertFn(urlPath, res.data.mediaFile.name)
      return
    }
    ElMessage.error(res.msg)
  }
}

// 组件销毁时，也及时销毁编辑器
onBeforeUnmount(() => {
  const editor = editorRef.value
  if (editor == null) return
  editor.destroy()
})

const handleCreated = (editor) => {
  editorRef.value = editor
  valueHtml.value = props.modelValue
}

const customPaste = (editor, event, callback) => {
  let htmlData = event.clipboardData.getData('text/html') // 获取粘贴的 html
  console.log(htmlData)
  let rtfData = event.clipboardData.getData('text/rtf') // 获取粘贴的 html
  console.log('-------------- rtfData --------------', rtfData)

  htmlData = htmlData.replace(/<\/?span[^>]*>/g, '');
  htmlData = htmlData.replace('<img', '<img referrerpolicy="no-referrer"')

  // 处理 word 内图片
  // 从html内容中查找粘贴内容中是否有图片元素，并返回img标签的属性src值的集合
  const imgSrcs = htmlData.match(/<img [^>]*src=['"]([^'"]+)[^>]*>/g);
  // 如果有
  if (imgSrcs && Array.isArray(imgSrcs) && imgSrcs.length) {
    console.log('imgSrcs', imgSrcs)
    // 从rtf内容中查找图片数据
    const rtfImageData = extractImageDataFromRtf(rtfData);
    console.log('rtfImageData', rtfImageData)
    // 如果找到
    if (rtfImageData.length) {
      // TODO：此处可以将图片上传到自己的服务器上,

      // 执行替换：将html内容中的img标签的src替换成ref中的图片数据，如果上面上传了则为图片路径
      htmlData = replaceImageFile(htmlData, imgSrcs, rtfImageData)
      console.log('+++++++++++++++++++ replace after +++++++++++++++++++', htmlData)
    }
  }

  editor.dangerouslyInsertHtml(htmlData);
  event.preventDefault()
  callback(false)
}

/**
 * 从rtf内容中匹配返回图片数据的集合
 * @param rtfData
 * @return Array
 */
const extractImageDataFromRtf = (rtfData) => {
  if (!rtfData) {
    return [];
  }
  const regexPictureHeader = /{\\pict[\s\S]+?({\\\*\\blipuid\s?[\da-fA-F]+)[\s}]*/
  const regexPicture = new RegExp('(?:(' + regexPictureHeader.source + '))([\\da-fA-F\\s]+)\\}', 'g')
  const images = rtfData.match(regexPicture)
  const result = ref([])
  if (images) {
    for (const image of images) {
      const imageType = ref(false)
      if (image.includes('\\pngblip')) {
        imageType.value = 'image/png'
      } else if (image.includes('\\jpegblip')) {
        imageType.value = 'image/jpeg'
      }
      if (imageType.value) {
        result.value.push({
          hex: image.replace(regexPictureHeader, '').replace(/[^\da-fA-F]/g, ''),
          type: imageType.value
        });
      }
    }
  }
  return result.value;
}

/**
 * 将html内容中img标签的属性值替换
 * @param htmlData html内容
 * @param imageSrcs html中img的属性src的值的集合
 * @param imagesHexSources rtf中图片数据的集合，与html内容中的img标签对应
 * @param isBase64Data 是否是Base64的图片数据
 * @return String
 */
const replaceImageFile = (htmlData, imageSrcs, imagesHexSources, isBase64Data = true) => {
  if (imageSrcs.length === imagesHexSources.length) {
    for (let i = 0; i < imageSrcs.length; i++) {
      const newSrc = isBase64Data
        ? `<img src='data:${imagesHexSources[i].type};base64,${convertHexToBase64(imagesHexSources[i].hex)}'/>`
        : imagesHexSources[i]
      htmlData = htmlData.replace(imageSrcs[i], newSrc);
    }
  }
  return htmlData;
}

/**
 * 十六进制转base64
 */
const convertHexToBase64 = (hexString) => {
  return btoa(hexString.match(/\w{2}/g).map(char => {
    return String.fromCharCode(parseInt(char, 16));
  }).join(''));
}


watch(() => props.modelValue, () => {
  valueHtml.value = props.modelValue
})

const isEmpty = () => {
  return editorRef.value.isEmpty()
}

defineExpose({ isEmpty })

</script>
