<template>
  <div ref="richArea" class="rich-wrapper">
    <Editor
      v-bind="$attrs"
      :id="id"
      ref="richText"
      model-events="change keyup undo redo setContent"
      :value="value"
      :class="['rich-text', isBorder ? 'is-border' : '']"
      :init="init"
      v-on="$listeners"
      @onBlur="handleRichTextBlur"
    />
    <div v-show="isShow" :style="contentmenu" class="contentmenu">
      <div v-if="isViewNote" @click="openExplanatoryNoteDialog">
        <SvgIcon icon-class="mark" size="14" />
        <span class="button"> 编辑注释</span>
      </div>
      <div v-else>
        <SvgIcon icon-class="slice" size="16" @click="setFill" />
        <span class="button" @click="setFill">设为填空</span>
        <span class="line"></span>
        <SvgIcon icon-class="close-circle" size="16" @click="deleteFill" />
        <span class="button" @click="deleteFill">删除填空</span>
      </div>
    </div>
    <MathDialog :visible.sync="isViewMathDialog" @confirm="mathConfirm" />
  </div>
</template>

<script>
import tinymce from 'tinymce/tinymce';
import Editor from '@tinymce/tinymce-vue';
import MathDialog from '@/components/MathDialog.vue';

import 'tinymce/icons/default/icons';
import 'tinymce/themes/silver';
// 引入富文本编辑器主题的js和css
import 'tinymce/themes/silver/theme.min';
import 'tinymce/skins/ui/oxide/skin.min.css';
// 扩展插件
import 'tinymce/plugins/image';
import 'tinymce/plugins/link';
// import 'tinymce/plugins/code';
// import 'tinymce/plugins/table';
import 'tinymce/plugins/lists';
// import 'tinymce/plugins/wordcount'; // 字数统计插件
import 'tinymce/plugins/media'; // 插入视频插件
// import 'tinymce/plugins/template'; // 模板插件
// import 'tinymce/plugins/fullscreen'; // 全屏插件
import 'tinymce/plugins/paste'; // 粘贴插件
// import 'tinymce/plugins/preview'; // 预览插件
import 'tinymce/plugins/hr';
import 'tinymce/plugins/autoresize'; // 自动调整大小插件
import 'tinymce/plugins/ax_wordlimit'; // 字数限制插件

import { getRandomNumber } from '@/utils';
import { isNodeType } from '@/utils/validate';
import { fileUpload } from '@/api/app';
import { addTone, handleToneValue } from '@/utils/common';

export default {
  name: 'RichText',
  components: {
    Editor,
    MathDialog,
  },
  inheritAttrs: false,
  props: {
    inline: {
      type: Boolean,
      default: false,
    },
    placeholder: {
      type: String,
      default: '输入内容',
    },
    value: {
      type: String,
      default: '',
    },
    height: {
      type: [Number, String],
      default: 52,
    },
    isBorder: {
      type: Boolean,
      default: false,
    },
    toolbar: {
      type: [String, Boolean],
      /* eslint-disable max-len */
      default:
        'fontselect fontsizeselect forecolor backcolor | lineheight underline | bold italic strikethrough alignleft aligncenter alignright | bullist numlist | image media | link blockquote hr mathjax',
    },
    wordlimitNum: {
      type: [Number, Boolean],
      default: 1000000,
    },
    isFill: {
      type: Boolean,
      default: false,
    },
    fontSize: {
      type: String,
      default: '12pt',
    },
    fontFamily: {
      type: String,
      default: 'Arial',
    },
    isHasSpace: {
      type: Boolean,
      default: false,
    },
    isViewPinyin: {
      type: Boolean,
      default: false,
    },
    pageFrom: {
      type: String,
      default: '',
    },
    isViewNote: {
      type: Boolean,
      default: false,
    },
    itemIndex: {
      type: Number,
      default: null,
    },
  },
  data() {
    return {
      isViewMathDialog: false,
      mathEleIsInit: true,
      math: '',
      isShow: false,
      contentmenu: {
        top: 0,
        left: 0,
      },
      id: getRandomNumber(),
      init: {
        content_style: `
          mjx-container, mjx-container * {
            font-size: 16px !important; /* 强制固定字体 */
            line-height: 1.2 !important; /* 避免行高影响 */
          }`, // 解决公式每点击一次字体就变大
        valid_elements: '*[*]', // 允许所有标签和属性
        valid_children: '+body[style]', // 允许 MathJax 的样式
        extended_valid_elements: 'span[*],mjx-container[*],svg[*],path[*]', // 明确允许 MathJax 标签
        inline: this.inline,
        font_size: this.fontSize,
        font_family: this.fontFamily,
        language_url: `${process.env.BASE_URL}tinymce/langs/zh_CN.js`,
        placeholder: this.placeholder,
        language: 'zh_CN',
        skin_url: `${process.env.BASE_URL}tinymce/skins/ui/oxide`,
        content_css: `${process.env.BASE_URL}tinymce/skins/content/${this.isHasSpace ? 'index-nospace' : 'index'}.css`,
        min_height: this.height,
        width: '100%',
        autoresize_bottom_margin: 0,
        plugins: 'link lists image hr media autoresize ax_wordlimit paste', // 移除 lineheight
        toolbar: this.toolbar, // 工具栏
        lineheight_formats: '0.5 1.0 1.2 1.5 2.0 2.5 3.0', // 行高选项（倍数）
        contextmenu: false, // 右键菜单
        menubar: false, // 菜单栏
        branding: false, // 品牌
        statusbar: false, // 状态栏
        entity_encoding: 'raw', // raw不编码任何字符;named: 使用命名实体(如 &nbsp;);numeric: 使用数字实体(如 &#160;)
        setup: (editor) => {
          editor.on('GetContent', (e) => {
            if (e.format === 'html') {
              e.content = this.smartPreserveLineBreaks(editor, e.content);
            }
          });

          let isRendered = false; // 标记是否已渲染
          editor.on('init', () => {
            editor.getBody().style.fontSize = this.init.font_size; // 设置默认字体大小
            editor.getBody().style.fontFamily = this.init.font_family; // 设置默认字体
          });

          // 自定义行高下拉（因为没有内置 lineheight 插件）
          editor.ui.registry.addMenuButton('lineheight', {
            text: '行高',
            tooltip: '行高',
            fetch: (callback) => {
              const formats = (this.init.lineheight_formats || '').split(/\s+/).filter(Boolean);
              const items = formats.map((v) => {
                return {
                  type: 'menuitem',
                  text: v,
                  onAction: () => {
                    try {
                      const name = `lineheight_${v.replace(/\./g, '_')}`;
                      // 动态注册格式（会覆盖同名注册，安全）
                      editor.formatter.register(name, {
                        inline: 'span',
                        styles: { lineHeight: v },
                      });
                      editor.formatter.apply(name);
                    } catch (err) {
                      // 容错处理
                    }
                  },
                };
              });
              callback(items);
            },
          });

          editor.on('click', () => {
            if (editor?.queryCommandState('ToggleToolbarDrawer')) {
              editor.execCommand('ToggleToolbarDrawer');
            }
            if (!isRendered && window.MathJax) {
              isRendered = true;
              window.MathJax.typesetPromise([editor.getBody()]);
            }
          });

          // 添加 MathJax 按钮
          editor.ui.registry.addButton('mathjax', {
            text: '∑',
            tooltip: '插入公式',
            onAction: () => {
              this.isViewMathDialog = true;
            },
          });

          // 内容变化时重新渲染公式
          editor.on('change', () => this.renderMath());

          editor.on('KeyDown', (e) => {
            // 检测删除或退格键
            if (e.keyCode === 8 || e.keyCode === 46) {
              // 延迟执行以确保删除已完成
              setTimeout(() => {
                this.cleanupRemovedAnnotations(editor);
              }, 500);
            }
          });

          // 也可以监听剪切操作
          editor.on('Cut', () => {
            setTimeout(() => {
              this.cleanupRemovedAnnotations(editor);
            }, 500);
          });
          // editor.on('NodeChange', function (e) {
          //   if (
          //     e.element &&
          //     e.element.tagName === 'SPAN' &&
          //     e.element.hasAttribute('data-annotation-id') &&
          //     (!e.element.textContent || /^\s*$/.test(e.element.textContent))
          //   ) {
          //     const annotationId = e.element.getAttribute('data-annotation-id');
          //     e.element.parentNode.removeChild(e.element);
          //     this.$emit('selectContentSetMemo', null, annotationId);
          //   }
          // });
        },
        font_formats:
          '楷体=楷体,微软雅黑;' +
          '黑体=黑体,微软雅黑;' +
          '宋体=宋体,微软雅黑;' +
          'Arial=arial,helvetica,sans-serif;' +
          'Times New Roman=times new roman,times,serif;' +
          '拼音=League;',
        fontsize_formats: '8pt 10pt 12pt 14pt 16pt 18pt 20pt 22pt 24pt 26pt 28pt 30pt 32pt 34pt 36pt',
        // 字数限制
        ax_wordlimit_num: this.wordlimitNum,
        ax_wordlimit_callback(editor) {
          editor.execCommand('undo');
        },
        media_filter_html: false,
        images_upload_handler: this.imagesUploadHandler,
        file_picker_types: 'media', // 文件上传类型
        file_picker_callback: this.filePickerCallback,
        init_instance_callback: this.isFill || this.isViewNote ? this.initInstanceCallback : '',
        paste_enable_default_filters: false, // 禁用默认的粘贴过滤器
        paste_as_text: true, // 默认作为纯文本粘贴
        // 粘贴预处理
        paste_preprocess(plugin, args) {
          let content = args.content;
          // 使用正则表达式去掉 style 中的 background 属性
          content = content.replace(/background(-color)?:[^;]+;/g, '');
          content = content.replace(/\t/g, '    '); // 将制表符替换为4个空格
          args.content = content;
        },
        // 指定在 WebKit 中粘贴时要保留的样式
        paste_webkit_styles:
          'display gap flex-wrap color min-height font font-size font-family font-weight width height margin-bottom margin padding line-height text-align border border-radius white-space',
      },
    };
  },
  watch: {
    isViewNote: {
      handler(newVal) {
        if (newVal) {
          let editor = tinymce.get(this.id);
          if (editor) {
            let start = editor.selection.getStart();
            this.$emit('selectNote', start.getAttribute('data-annotation-id'));
          }
        }
      },
    },
    fontSize: {
      handler(newVal) {
        const editor = tinymce.get(this.id);
        if (!editor || typeof editor.execCommand !== 'function') return;

        const applyFontSize = () => {
          try {
            editor.execCommand('FontSize', false, newVal);
            editor.setContent(this.value); // 触发内容更新，解决 value 在设置后变为空字符串的问题
          } catch (e) {
            // 容错：某些情况下 execCommand 会抛错，忽略即可
            // console.warn('apply fontSize failed', e);
          }
        };

        // 如果 selection 暂不可用，延迟一次执行以避免其他监听器中访问 selection 报错
        if (!editor.selection || typeof editor.selection.getRng !== 'function') {
          setTimeout(applyFontSize, 100);
        } else {
          applyFontSize();
        }
      },
    },
    fontFamily: {
      handler(newVal) {
        const editor = tinymce.get(this.id);
        if (!editor || typeof editor.execCommand !== 'function') return;

        const applyFontFamily = () => {
          try {
            editor.execCommand('FontName', false, newVal);
            editor.setContent(this.value); // 触发内容更新，解决 value 在设置后变为空字符串的问题
          } catch (e) {
            // 容错：忽略因 selection 不可用或其它原因导致的错误
          }
        };

        // 如果 selection 暂不可用，延迟执行
        if (!editor.selection || typeof editor.selection.getRng !== 'function') {
          setTimeout(applyFontFamily, 100);
        } else {
          applyFontFamily();
        }
      },
    },
  },
  created() {
    if (this.pageFrom !== 'audit') {
      window.addEventListener('click', this.hideToolbarDrawer);
    }

    if (this.isFill || this.isViewNote) {
      window.addEventListener('click', this.hideContentmenu);
    }
    this.setBackgroundColor();
  },
  beforeDestroy() {
    if (this.pageFrom !== 'audit') {
      window.removeEventListener('click', this.hideToolbarDrawer);
    }

    if (this.isFill || this.isViewNote) {
      window.removeEventListener('click', this.hideContentmenu);
    }
  },
  methods: {
    smartPreserveLineBreaks(editor, content) {
      let body = editor.getBody();
      let originalParagraphs = Array.from(body.getElementsByTagName('p'));

      let tempDiv = document.createElement('div');
      tempDiv.innerHTML = content;
      let outputParagraphs = Array.from(tempDiv.getElementsByTagName('p'));

      outputParagraphs.forEach((outputP, index) => {
        let originalP = originalParagraphs[index];

        if (originalP && outputP.innerHTML === '') {
          // 判断这个空段落是否应该包含 <br>
          let shouldHaveBr = this.shouldPreserveLineBreak(originalP, index, originalParagraphs);
          if (shouldHaveBr) {
            outputP.innerHTML = '<br>';
          }
        }
      });

      return tempDiv.innerHTML;
    },

    shouldPreserveLineBreak(paragraph, index, allParagraphs) {
      // 规则1：如果段落原本包含 <br>
      if (paragraph.innerHTML.includes('<br>')) {
        return true;
      }

      // 规则2：如果段落位于内容中间（不是第一个或最后一个）
      if (index > 0 && index < allParagraphs.length - 1) {
        let prevHasContent = allParagraphs[index - 1].textContent.trim() !== '';
        let nextHasContent = allParagraphs[index + 1].textContent.trim() !== '';

        if (prevHasContent && nextHasContent) {
          return true;
        }
      }

      // 规则3：如果段落是通过回车创建的（前后有内容）
      let isBetweenContent = false;
      if (index > 0 && allParagraphs[index - 1].textContent.trim() !== '') {
        isBetweenContent = true;
      }
      if (index < allParagraphs.length - 1 && allParagraphs[index + 1].textContent.trim() !== '') {
        isBetweenContent = true;
      }

      return isBetweenContent;
    },

    // 设置背景色
    setBackgroundColor() {
      let iframes = document.getElementsByTagName('iframe');
      for (let i = 0; i < iframes.length; i++) {
        let iframe = iframes[i];
        // 获取 <iframe> 内部的文档对象
        let iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
        let bodyElement = iframeDocument.body;
        if (bodyElement) {
          // 设置背景色
          bodyElement.style.backgroundColor = '#f2f3f5';
        }
      }
    },
    /**
     * 判断内容是否全部加粗
     */
    isAllBold() {
      let editor = tinymce.get(this.id);
      let body = editor.getBody();
      function getTextNodes(node) {
        let textNodes = [];
        if (node.nodeType === 3 && node.nodeValue.trim() !== '') {
          textNodes.push(node);
        } else {
          for (let child of node.childNodes) {
            textNodes = textNodes.concat(getTextNodes(child));
          }
        }
        return textNodes;
      }
      let textNodes = getTextNodes(body);
      if (textNodes.length === 0) return false;
      return textNodes.every((node) => {
        let el = node.parentElement;
        while (el && el !== body) {
          const tag = el.tagName.toLowerCase();
          const fontWeight = window.getComputedStyle(el).fontWeight;
          if (tag === 'b' || tag === 'strong' || fontWeight === 'bold' || parseInt(fontWeight) >= 600) {
            return true;
          }
          el = el.parentElement;
        }
        return false;
      });
    },
    /**
     * 设置整体富文本格式
     * @param {string} type 格式名称
     * @param {string} val 格式值
     */
    setRichFormat(type, val) {
      let editor = tinymce.get(this.id);
      if (!editor) return;
      editor.execCommand('SelectAll');

      switch (type) {
        case 'bold': {
          if (this.isAllBold()) {
            editor.formatter.remove('bold');
          } else {
            editor.formatter.apply('bold');
          }
          break;
        }

        case 'fontSize':
        case 'lineHeight':
        case 'color':
        case 'fontFamily': {
          editor.formatter.register('my_customformat', {
            inline: 'span',
            styles: { [type]: val },
          });

          editor.formatter.apply('my_customformat');
          break;
        }

        case 'align': {
          if (val === 'LEFT') {
            editor.execCommand('JustifyLeft');
          } else if (val === 'MIDDLE') {
            editor.execCommand('JustifyCenter');
          } else if (val === 'RIGHT') {
            editor.execCommand('JustifyRight');
          }
          break;
        }

        default: {
          editor.formatter.toggle(type);
        }
      }
      editor.selection.collapse(false);
    },
    /**
     * 图片上传自定义逻辑函数
     * @param {object} blobInfo 文件数据
     * @param {Function} success 成功回调函数
     * @param {Function} fail 失败回调函数
     */
    imagesUploadHandler(blobInfo, success, fail) {
      let file = blobInfo.blob();
      const formData = new FormData();
      formData.append(file.name, file, file.name);
      fileUpload('Mid', formData, { isGlobalprogress: true })
        .then(({ file_info_list }) => {
          if (file_info_list.length > 0) {
            success(file_info_list[0].file_url_open);
          } else {
            fail('上传失败');
          }
        })
        .catch(() => {
          fail('上传失败');
        });
    },
    /**
     * 文件上传自定义逻辑函数
     * @param {Function} callback
     * @param {String} value
     * @param {object} meta
     */
    filePickerCallback(callback, value, meta) {
      if (meta.filetype === 'media') {
        let filetype = '.mp3, .mp4';
        let input = document.createElement('input');
        input.setAttribute('type', 'file');
        input.setAttribute('accept', filetype);
        input.click();
        input.addEventListener('change', () => {
          let file = input.files[0];
          const formData = new FormData();
          formData.append(file.name, file, file.name);
          fileUpload('Mid', formData, { isGlobalprogress: true })
            .then(({ file_info_list }) => {
              if (file_info_list.length > 0) {
                callback(file_info_list[0].file_url_open);
              } else {
                callback('');
              }
            })
            .catch(() => {
              callback('');
            });
        });
      }
    },
    /**
     * 初始化编辑器实例回调函数
     * @param {Editor} editor 编辑器实例
     */
    initInstanceCallback(editor) {
      editor.on('SetContent Undo Redo ViewUpdate keyup mousedown', () => {
        this.hideContentmenu();
      });

      editor.on('click', (e) => {
        if (e.target.classList.contains('rich-fill')) {
          editor.selection.select(e.target); // 选中填空
          let { offsetLeft, offsetTop } = e.target;
          this.showContentmenu({
            pixelsFromLeft: offsetLeft - 14,
            pixelsFromTop: offsetTop,
          });
        }
      });

      let mouseX = 0;
      editor.on('mousedown', (e) => {
        mouseX = e.offsetX;
      });

      editor.on('mouseup', (e) => {
        let start = editor.selection.getStart();
        let end = editor.selection.getEnd();
        let rng = editor.selection.getRng();
        if (start !== end || rng.collapsed) {
          this.hideContentmenu();
          return;
        }
        if (e.offsetX < mouseX) {
          mouseX = e.offsetX;
        }
        if (isNodeType(start, 'span')) {
          start = start.parentNode;
        }

        // 获取文本内容和起始偏移位置
        let text = start.textContent;
        let startOffset = rng.startOffset;
        let previousSibling = rng.startContainer.previousSibling;
        // 判断是否选中的是 span 标签
        let isSpan = isNodeType(rng.startContainer.parentNode, 'span');
        if (isSpan) {
          previousSibling = rng.startContainer.parentNode.previousSibling;
        }
        // 计算起始偏移位置
        while (previousSibling) {
          startOffset += previousSibling.textContent.length;
          previousSibling = previousSibling.previousSibling;
        }

        // 获取起始偏移位置前的文本内容
        const textBeforeOffset = text.substring(0, startOffset);

        /* 使用 Canvas API测量文本宽度 */
        // 获取字体大小和行高
        let computedStyle = window.getComputedStyle(start);
        const fontSize = parseFloat(computedStyle.fontSize);
        const canvas = document.createElement('canvas');
        const context = canvas.getContext('2d');
        context.font = `${fontSize}pt ${computedStyle.fontFamily}`;
        // 计算文字距离左侧的像素位置
        const width = context.measureText(textBeforeOffset).width;
        const lineHeight = context.measureText('M').fontBoundingBoxAscent + 3.8; // 获取行高

        /* 计算偏移位置 */
        const computedWidth = computedStyle.width.replace('px', ''); // 获取编辑器宽度
        let row = width / computedWidth; // 计算选中文本在第几行
        row = row % 1 > 0.8 ? Math.ceil(row) : Math.floor(row);
        const offsetTop = start.offsetTop; // 获取选中文本距离顶部的像素位置
        let pixelsFromTop = offsetTop + lineHeight * row; // 计算选中文本距离顶部的像素位置
        this.showContentmenu({
          pixelsFromLeft: mouseX,
          pixelsFromTop,
        });
      });
    },
    // 删除填空
    deleteContent() {
      let editor = tinymce.get(this.id);
      let start = editor.selection.getStart();
      if (isNodeType(start, 'span')) {
        let textContent = start.textContent;
        let content = editor.selection.getContent();
        let str = textContent.split(content);
        start.remove();
        editor.selection.setContent(str.join(content));
      } else {
        this.collapse();
      }
    },
    // 设置填空
    setContent() {
      let editor = tinymce.get(this.id);
      let start = editor.selection.getStart();
      let content = editor.selection.getContent();
      if (isNodeType(start, 'span')) {
        let textContent = start.textContent;
        let str = textContent.split(content);
        start.remove();
        editor.selection.setContent(str.join(this.getSpanString(content)));
      } else {
        let str = this.replaceSpanString(content);
        editor.selection.setContent(this.getSpanString(str));
      }
    },
    // 折叠选区
    collapse() {
      let editor = tinymce.get(this.id);
      let rng = editor.selection.getRng();
      if (!rng.collapsed) {
        this.hideContentmenu();
        editor.selection.collapse();
      }
    },
    // 获取 span 标签
    getSpanString(str) {
      return `<span class="rich-fill" style="text-decoration: underline;cursor: pointer;">${str}</span>`;
    },
    // 去除 span 标签
    replaceSpanString(str) {
      return str.replace(/<span\b[^>]*>(.*?)<\/span>/gi, '$1');
    },
    createParsedTextInfoPinyin(content) {
      var styles = this.getFirstCharStyles();
      let text = content.replace(/<[^>]+>/g, '');
      this.$emit('createParsedTextInfoPinyin', text, styles);
    },
    handleRichTextBlur() {
      this.$emit('handleRichTextBlur', this.itemIndex);
      let content = tinymce.get(this.id).getContent();

      if (this.isViewPinyin) {
        this.createParsedTextInfoPinyin(content);
        return;
      }

      // 判断富文本中是否有 字母+数字+空格 的组合，如果没有，直接返回
      let isHasPinyin = content
        .split(/<[^>]+>/g)
        .filter((item) => item)
        .some((item) => item.match(/[a-zA-Z]+\d+(\s|&nbsp;)+/));

      if (!isHasPinyin) {
        return;
      }
      // 用标签分割富文本，保留标签
      let reg = /(<[^>]+>)/g;

      let text = content
        .split(reg)
        .filter((item) => item)
        // 如果是标签，直接返回
        // 如果是文本，将文本按空格分割为数组，如果是拼音，将拼音转为带音调的拼音
        .map((item) => {
          // 重置正则，使用全局匹配 g 时需要重置，否则会出现匹配不到的情况，因为 lastIndex 会一直累加，test 方法会从 lastIndex 开始匹配
          reg.lastIndex = 0;
          if (reg.test(item)) {
            return item;
          }
          return item.split(/\s+/).map((item) => handleToneValue(item));
        })
        // 如果是标签，直接返回
        // 二维数组，转为拼音，并打平为一维数组
        .map((item) => {
          if (/<[^>]+>/g.test(item)) return item;

          return item
            .map(({ number, con }) => (number && con ? addTone(Number(number), con) : number || con || ''))
            .flat();
        })
        // 如果是数组，将数组字符串每两个之间加一个空格
        .map((item) => {
          if (typeof item === 'string') return item;
          return item.join(' ');
        })
        .join('');

      // 更新 v-model
      this.$emit('input', text);
    },
    // 设置填空
    setFill() {
      this.setContent();
      this.hideContentmenu();
    },
    // 删除填空
    deleteFill() {
      this.deleteContent();
      this.hideContentmenu();
    },
    // 隐藏工具栏抽屉
    hideToolbarDrawer() {
      let editor = tinymce.get(this.id);
      if (editor.queryCommandState('ToggleToolbarDrawer')) {
        editor.execCommand('ToggleToolbarDrawer');
      }
    },
    // 隐藏填空右键菜单
    hideContentmenu() {
      this.isShow = false;
    },
    showContentmenu({ pixelsFromLeft, pixelsFromTop }) {
      this.isShow = true;
      this.contentmenu = {
        left: `${this.isViewNote ? pixelsFromLeft : pixelsFromLeft + 14}px`,
        top: `${this.isViewNote ? pixelsFromTop + 62 : pixelsFromTop + 22}px`,
      };
    },

    mathConfirm(math) {
      let editor = tinymce.get(this.id);
      let tmpId = getRandomNumber();
      editor.insertContent(`
         <span id="${tmpId}" contenteditable="false" class="mathjax-container editor-math">
           ${math}
         </span>
       `);
      this.mathEleIsInit = false;
      this.renderMath(tmpId);
      this.isViewMathDialog = false;
    },
    // 渲染公式
    async renderMath(id) {
      if (this.mathEleIsInit) return; // 如果公式已经渲染过，返回
      if (window.MathJax) {
        let editor = tinymce.get(this.id);
        let eleMathArs = [];
        if (id) {
          // 插入的时候，会传递ID，执行单个渲染
          let ele = editor.dom.select(`#${id}`)[0];
          eleMathArs = [ele];
        } else {
          // 否则，查询编辑器里面所有的公式
          eleMathArs = editor.dom.select(`.editor_math`);
        }
        if (eleMathArs.length === 0) return;
        await this.$nextTick();
        window.MathJax.typesetPromise(eleMathArs).catch((err) =>
          /* eslint-disable */ console.error(...oo_tx(`483836707_818_65_818_101_11`, 'MathJax error:', err))
        );
        this.mathEleIsInit = true;
      }
    },

    // 获取高亮 span 标签
    getLightSpanString(noteId, str) {
      return `<span data-annotation-id="${noteId}" class="rich-fill" style="background-color:#fff3b7;cursor: pointer;">${str}</span>`;
    },
    // 选中文本打开弹窗
    openExplanatoryNoteDialog() {
      let editor = tinymce.get(this.id);
      let start = editor.selection.getStart();
      this.$emit('view-explanatory-note', { visible: true, noteId: start.getAttribute('data-annotation-id') });
      this.hideContentmenu();
    },
    // 设置高亮背景，并保留备注
    setExplanatoryNote(richData) {
      let noteId = '';
      let editor = tinymce.get(this.id);
      let start = editor.selection.getStart();
      let content = editor.selection.getContent();
      if (isNodeType(start, 'span')) {
        noteId = start.getAttribute('data-annotation-id');
      } else {
        noteId = `${this.id}_${Math.floor(Math.random() * 1000000)
          .toString()
          .padStart(10, '0')}`;
        let str = this.replaceSpanString(content);
        editor.selection.setContent(this.getLightSpanString(noteId, str));
      }
      let selectText = content.replace(/<[^>]+>/g, '');
      let note = { id: noteId, note: richData.note, selectText };
      this.$emit('selectContentSetMemo', note);
    },
    // 取消注释
    cancelExplanatoryNote() {
      let editor = tinymce.get(this.id);
      let start = editor.selection.getStart();
      if (isNodeType(start, 'span')) {
        let textContent = start.textContent;
        let content = editor.selection.getContent();
        let str = textContent.split(content);
        start.remove();
        editor.selection.setContent(str.join(content));
        this.$emit('selectContentSetMemo', null, start.getAttribute('data-annotation-id'));
      } else {
        this.collapse();
      }
    },
    // 删除，监听处理备注
    cleanupRemovedAnnotations(editor) {
      if (!this.isViewNote) return; // 只有富文本才处理
      const body = editor.getBody();
      const annotations = body.querySelectorAll('span[data-annotation-id]');

      this.handleEmptySpan(editor);

      // 存储所有现有的注释ID
      const existingIds = new Set();
      annotations.forEach((span) => {
        existingIds.add(span.getAttribute('data-annotation-id'));
      });

      // 与你存储的注释数据对比，清理不存在的
      this.$emit('compareAnnotationAndSave', existingIds);
    },
    // 删除span里面的文字之后，会出现空 span 标签残留，需处理掉
    handleEmptySpan(editor) {
      const selection = editor.selection;
      const selectedNode = selection.getNode();
      // 如果选中的是注释span内的内容
      if (selectedNode.nodeType === 1 && selectedNode.hasAttribute('data-annotation-id')) {
        const span = selectedNode;
        // 检查删除后是否为空
        if (!span.textContent || /^\s*$/.test(span.textContent)) {
          // 保存注释ID
          const annotationId = span.getAttribute('data-annotation-id');

          // 用其父节点替换span
          span.parentNode.replaceChild(document.createTextNode(''), span);

          // 从存储中移除注释
          this.$emit('selectContentSetMemo', null, annotationId);
        }
      }
    },

    getFirstCharStyles() {
      const editor = tinymce.activeEditor;
      if (!editor) return {};

      const firstTextNode = this.findFirstTextNode(editor.getBody());
      if (!firstTextNode) return {};

      const styles = {};
      let element = firstTextNode.parentElement;

      while (element && element !== editor.getBody().parentElement) {
        const computed = window.getComputedStyle(element);

        if (!styles.fontFamily && computed.fontFamily && computed.fontFamily !== 'inherit') {
          styles.fontFamily = computed.fontFamily;
        }

        if (!styles.fontSize && computed.fontSize && computed.fontSize !== 'inherit') {
          const fontSize = computed.fontSize;
          const pxValue = parseFloat(fontSize);
          if (!isNaN(pxValue)) {
            // px转pt公式：pt = px * 3/4
            const ptValue = Math.round(pxValue * 0.75 * 10) / 10;
            styles.fontSize = ptValue + 'pt';
          } else {
            styles.fontSize = fontSize;
          }
        }

        if (!styles.color && computed.color && computed.color !== 'inherit') {
          styles.color = computed.color;
        }

        if (!styles.bold && (computed.fontWeight === 'bold' || computed.fontWeight === '700')) {
          styles.bold = true;
          styles.fontWeight = 'bold';
        }

        if (!styles.underline && computed.textDecoration.includes('underline')) {
          styles.underline = true;
          styles.textDecoration = 'underline';
        }

        if (!styles.strikethrough && computed.textDecoration.includes('line-through')) {
          styles.strikethrough = true;
          styles.textDecoration = 'line-through';
        }

        if (Object.keys(styles).length >= 6) break;

        element = element.parentElement;
      }
      return styles;
    },

    findFirstTextNode(element) {
      const walker = document.createTreeWalker(
        element,
        NodeFilter.SHOW_TEXT,
        {
          acceptNode: (node) => (node.textContent.trim() ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT),
        },
        false
      );
      return walker.nextNode();
    },
  },
};
</script>

<style lang="scss" scoped>
.rich-text {
  :deep + .tox {
    .tox-sidebar-wrap {
      border: 1px solid $fill-color;
      border-radius: 4px;

      &:hover {
        border-color: #c0c4cc;
      }
    }

    &.tox-tinymce {
      border-width: 0;
      border-radius: 0;

      .tox-edit-area__iframe {
        background-color: $fill-color;
      }
    }

    &:not(.tox-tinymce-inline) .tox-editor-header {
      box-shadow: none;
    }
  }

  &.is-border {
    :deep + .tox.tox-tinymce {
      border: $border;
      border-radius: 4px;
    }
  }
}

.contentmenu {
  position: absolute;
  z-index: 999;
  display: flex;
  column-gap: 4px;
  align-items: center;
  padding: 4px 8px;
  font-size: 14px;
  background-color: #fff;
  border-radius: 2px;
  box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 10%);

  .svg-icon,
  .button {
    cursor: pointer;
  }

  .line {
    min-height: 16px;
    margin: 0 4px;
  }
}
</style>
