/**
 * @description insert formula menu
 * @author wangfupeng
 */

import { IModalMenu } from '@wangeditor/editor'
import './custom.css'
import {
  DomEditor,
  IDomEditor,
  SlateNode,
  SlateRange,
  t,
  genModalTextareaElems,
  genModalButtonElems,
} from '@wangeditor/editor'
import { SIGMA_SVG } from '../../constants/icon-svg'
import $, { Dom7Array, DOMElement } from '../../utils/dom'
import { genRandomStr } from '../../utils/util'
import { FormulaElement } from '../custom-types'
import { customSymbols, customFormula } from '../../utils/actionBut'
import katex from 'katex'

/**
 * 生成唯一的 DOM ID
 */
function genDomID(): string {
  return genRandomStr('w-e-insert-formula')
}

class InsertFormulaMenu implements IModalMenu {
  readonly title = t('formula.insert')
  readonly iconSvg = SIGMA_SVG
  readonly tag = 'button'
  readonly showModal = true // 点击 button 时显示 modal
  readonly modalWidth = 300
  private $content: Dom7Array | null = null
  private readonly textareaId = genDomID()
  private readonly buttonId = genDomID()
  // 插入默认格式公式class组名
  private readonly formatClass = genDomID()
  // 预览区域id
  private readonly previewId = genDomID()

  getValue(editor: IDomEditor): string | boolean {
    // 插入菜单，不需要 value
    return ''
  }

  isActive(editor: IDomEditor): boolean {
    // 任何时候，都不用激活 menu
    return false
  }

  exec(editor: IDomEditor, value: string | boolean) {
    // 点击菜单时，弹出 modal 之前，不需要执行其他代码
    // 此处空着即可
  }

  isDisabled(editor: IDomEditor): boolean {
    const { selection } = editor
    if (selection == null) return true
    if (SlateRange.isExpanded(selection)) return true // 选区非折叠，禁用

    const selectedElems = DomEditor.getSelectedElems(editor)

    const hasVoidElem = selectedElems.some(elem => editor.isVoid(elem))
    if (hasVoidElem) return true // 选中了 void 元素，禁用

    const hasPreElem = selectedElems.some(elem => DomEditor.getNodeType(elem) === 'pre')
    if (hasPreElem) return true // 选中了 pre 原则，禁用

    return false
  }

  getModalPositionNode(editor: IDomEditor): SlateNode | null {
    return null // modal 依据选区定位
  }

  getModalContentElem(editor: IDomEditor): DOMElement {
    const { textareaId, buttonId, previewId, formatClass } = this
    // eslint-disable-next-line no-debugger
    const [textareaContainerElem, textareaElem] = genModalTextareaElems(
      t('formula.formula'),
      textareaId,
      t('formula.placeholder')
    )
    const $textarea = $(textareaElem)
    const [buttonContainerElem] = genModalButtonElems(buttonId, t('formula.ok'))

    if (this.$content == null) {
      // 第一次渲染
      const $content = $('<div></div>')

      // 绑定事件（第一次渲染时绑定，不要重复绑定）
      $content.on('click', `#${buttonId}`, e => {
        e.preventDefault()
        const value = $content.find(`#${textareaId}`).val().trim()
        this.insertFormula(editor, value, $content)
        editor.hidePanelOrModal() // 隐藏 modal
      })

      // 绑定插入事件
      $content.on('click', `.${buttonId}`, e => {
        e.preventDefault()
        const value = $content.find(`#${textareaId}`).val().trim()
        this.insertFormula(editor, value, $content)
      })

      // 预设字符输入点击
      $content.on('click', '.formula_text', e => {
        e.preventDefault()
        console.log(document.activeElement)
        const target = e.target as EventTarget | null
        if (target) {
          // 获取textarea内容
          const textStr = $content.find(`#${textareaId}`).val().trim()
          // 获取当前textarea光标位置
          const textarea = $content.find(`#${textareaId}`)[0] as HTMLTextAreaElement
          const activeIndex = textarea.selectionStart
          // 获取当前点击的字符内容
          const value = (target as HTMLElement).dataset.formulaValue
          // 拼接字符串
          const resultTest = textStr.slice(0, activeIndex) + value + textStr.slice(activeIndex)
          $content.find(`#${textareaId}`).val(resultTest)
          // 展示区域
          const displayAreaElem = $content.find(`#${previewId}`)[0] as HTMLTextAreaElement
          this.displayAreainsertFormula(displayAreaElem, resultTest, $content)
        }
      })

      // 监听输入框内容变动
      $content.on('input', `#${textareaId}`, e => {
        const textStr = $content.find(`#${textareaId}`).val().trim()
        // 展示区域
        const displayAreaElem = $content.find(`#${previewId}`)[0] as HTMLTextAreaElement
        this.displayAreainsertFormula(displayAreaElem, textStr, $content)
      })

      // 记录属性，重要
      this.$content = $content
    }

    const $content = this.$content
    $content.html('') // 先清空内容
    // 常用公式
    const commonFormulasElem = this.additionalFeatures()
    // 展示区域
    const displayArea = this.contentDisplayArea()
    //  常用符号
    const commonSymbolsElem = this.additionalTopFeatures()

    // 展示区域
    // append textarea and button

    const $editAreaWrap = $('<div class="edit_area_wrap"></div>')
    const $editArea = $('<div id="' + formatClass + '"></div>')

    // 插入位置
    $content.append(commonSymbolsElem)

    // 输入
    $editArea.append(textareaContainerElem)
    // 展示
    $editArea.append(displayArea)
    $editAreaWrap.append($editArea)
    // 常用公式
    $editAreaWrap.append(commonFormulasElem)

    $content.append($editAreaWrap)
    // 确认按钮
    $content.append(buttonContainerElem)

    // 设置 input val
    $textarea.val('')

    // focus 一个 input（异步，此时 DOM 尚未渲染）
    setTimeout(() => {
      $textarea.focus()
    })

    return $content[0]
  }

  // 自定义内容展示区
  private contentDisplayArea() {
    const { previewId } = this
    const $addition = $('<div class="content_display_area" id="' + previewId + '"></div>')
    return $addition
  }

  // 自定义加入插入/编辑弹窗内容（常用符号）
  private additionalTopFeatures() {
    let htmlStr = '<div class="formula_text_top_wrap">'
    customSymbols.forEach(item => {
      htmlStr += `<div class="formula_text_top_wrap"><div data-formula-value="${item.value}" class="formula_text">${item.name}</div></div>`
    })
    htmlStr += '</div>'
    const $addition = $(htmlStr)
    return $addition
  }

  // 自定义加入插入/编辑弹窗内容（常用公式）
  private additionalFeatures() {
    const $addition = $(
      '<div class="formula_text_wrap"><div data-formula-value="\\dfrac{a}{b}\\pm \\dfrac{c}{d}= \\dfrac{ad \\pm bc}{bd}" class="formula_text">2222</div></div>'
    )
    return $addition
  }

  // 展示区显示内容
  private displayAreainsertFormula(show: Element, value: string, content: any) {
    const { previewId } = this
    if (!value) return
    show.innerHTML = ''

    const span = document.createElement('span')
    span.style.display = 'inline-block'
    content.find(`#${previewId}`)[0].append(span)
    katex.render(value, span, {
      throwOnError: false,
    })
  }

  private insertFormula(editor: IDomEditor, value: string, content: any) {
    if (!value) return

    // 还原选区
    editor.restoreSelection()

    if (this.isDisabled(editor)) return

    const formulaElem: FormulaElement = {
      type: 'formula',
      value,
      children: [{ text: '' }], // void node 需要有一个空 text
    }
    const span = document.createElement('span')
    span.style.display = 'inline-block'
    katex.render(value, span, {
      throwOnError: false,
    })

    editor.insertNode(formulaElem)
  }
}

export default InsertFormulaMenu
