<template>
  <div class="border border-solid border-gray-100 h-full" v-loading="loadingShow">
    <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'
import { getImgData } from '@/api/article'

const loadingShow = ref(false)
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) => {
  // console.log(event)
  // 显示蒙版
  loadingShow.value = true
  let htmlData = event.clipboardData.getData('text/html') // 获取粘贴的 html
  // console.log(htmlData)

  if (htmlData == "") {
    // 如果是纯文本
    htmlData = event.clipboardData.getData('text/plain') // 获取粘贴的 html
    // 将 \n 替换为 p 标签
    htmlData.replace(/\r/g, '');
    htmlData = '<p>' + htmlData.replace(/\n/g, '</p><p>') + '</p>'
    //无图片，直接复制
    editor.dangerouslyInsertHtml(htmlData);
    loadingShow.value = false
    callback(false)
    event.preventDefault()
    return
  }

  let rtfData = event.clipboardData.getData('text/rtf') // 获取粘贴的 html
  // console.log('-------------- rtfData --------------', rtfData)

  htmlData = htmlData.replace(/<\/?span[^>]*>/g, '');
  htmlData = htmlData.replace(/<\/?font[^>]*>/g, '');
  // htmlData = htmlData.replace(/<p[^>]*(?:>|\s[^>]*>)/gi, '<p>');
  htmlData = htmlData.replace(/<img/g, '<img referrerpolicy="no-referrer"')
  // console.log('111111111111 replace after 1111111111', htmlData)

  // 从html内容中查找粘贴内容中是否有图片元素，并返回img标签的属性src值的集合
  const imgSrcs = htmlData.match(/<img [^>]*src=['"]([^'"]+)[^>]*>/g);
  if (imgSrcs && Array.isArray(imgSrcs) && imgSrcs.length) {
    //有图片，处理图片
    // console.log('imgSrcs', imgSrcs)
    if (rtfData !== "") {
      // 处理 word 内图片
      // 从rtf内容中查找图片数据
      const rtfImageData = extractImageDataFromRtf(rtfData);
      // 如果找到
      if (rtfImageData.length) {
        // 执行替换：将html内容中的img标签的src替换成ref中的图片数据，如果上面上传了则为图片路径
        htmlData = replaceImageFile(htmlData, imgSrcs, rtfImageData)
        // console.log('22222222222222222 ----------- 22222222222222222', htmlData)
        editor.dangerouslyInsertHtml(htmlData);
        loadingShow.value = false
        callback(false)
      }
    } else {
      convertImgToBase64(imgSrcs).then(imgList => {
        imgList.forEach(item => {
          htmlData = htmlData.replace(item.src, item.tar)
        })
        // console.log('33333333333333333 ----------- 33333333333333333', htmlData)
        editor.dangerouslyInsertHtml(htmlData);
        // 隐藏蒙版
        loadingShow.value = false
        callback(false)
      })
    }
  } else {
    //无图片，直接复制
    editor.dangerouslyInsertHtml(htmlData);
    loadingShow.value = false
    callback(false)
  }
  event.preventDefault()
}

const convertImgToBase64 = async (data) => {
  let replaces = []
  for (let i = 0; i < data.length; i++) {
    const imgSrc = data[i].match(/src=['"]([^'"]+)['"]/)
    if (!imgSrc) {
      return
    }
    // 发到服务器上并转为base64
    const res = await getImgData({ url: imgSrc[1] })
    if (res && res.data) {
      replaces.push({ src: data[i], tar: `<img src='${res.data.imgData}' />` })
    }
  }

  return replaces
}

/**
 * 从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) {
      let imageType = ''
      if (image.includes('\\pngblip')) {
        imageType = 'image/png'
      } else if (image.includes('\\jpegblip')) {
        imageType = 'image/jpeg'
      }
      if (imageType) {
        result.value.push({
          hex: image.replace(regexPictureHeader, '').replace(/[^\da-fA-F]/g, ''),
          type: imageType
        });
      }
    }
  }
  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()
}

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

defineExpose({ isEmpty, getHtml })

</script>
