import { DomEditor, SlateTransforms } from '@wangeditor/editor'
import { h } from 'snabbdom'
import { throttle } from 'lodash-es'
import $ from 'jquery'
import { getStyleValue } from '@/utils/common'

import ImageAutoConf, { imageWidth100MenuChapterConf, imageWidth50MenuChapterConf, imageWidth30MenuChapterConf } from '../customer/Image'

const withImageNode = (editor) => {
  const { isInline, isVoid, normalizeNode } = editor
  const newEditor = editor

  newEditor.isInline = (elem) => {
    const type = DomEditor.getNodeType(elem)
    if (type === 'chapterImage') return true // 设置为 inline
    return isInline(elem)
  }

  newEditor.isVoid = (elem) => {
    const type = DomEditor.getNodeType(elem)
    if (type === 'chapterImage') return true // 设置为 void
    return isVoid(elem)
  }

  // 重新 normalize
  newEditor.normalizeNode = ([node, path]) => {
    const type = DomEditor.getNodeType(node)
    if (type !== 'chapterImage') {
      // 未命中 chapterImage ，执行默认的 normalizeNode
      return normalizeNode([node, path])
    }

    // editor 顶级 node
    const topLevelNodes = newEditor.children || []

    //  后面必须跟一个 p header blockquote（否则后面无法继续输入文字）
    const nextNode = topLevelNodes[path[0] + 1] || {}
    const nextNodeType = DomEditor.getNodeType(nextNode)
    if (nextNodeType !== 'paragraph' && nextNodeType !== 'blockquote' && !nextNodeType.startsWith('header')) {
      // link-card node 后面不是 p 或 header ，则插入一个空 p
      const p = { type: 'paragraph', children: [{ text: '' }] }
      const insertPath = [path[0] + 1]
      SlateTransforms.insertNodes(newEditor, p, {
        at: insertPath // 在 link-card 后面插入
      })
    }
  }

  return newEditor // 返回 newEditor ，重要！！！
}

function genContainerId(editor, elemNode) {
  const { id } = DomEditor.findKey(editor, elemNode) // node 唯一 id
  return `w-e-image-container-${id}`
}

const renderImageContainer = (editor, elemNode, imageVnode, imageInfo) => {
  const { width, height } = imageInfo
  const style = {}
  if (width) style.width = width
  if (height) style.height = height

  const containerId = genContainerId(editor, elemNode)
  return h('div', {
    props: { id: containerId, className: 'w-e-image-container', innerHTML: imageVnode },
    style: style
  })
}

/**
 * 选中状态下，渲染 image container（渲染拖拽容器，修改图片尺寸）
 */
function renderResizeContainer(editor, elemNode, imageVnode, imageInfo) {
  const $body = $('body')
  const containerId = genContainerId(editor, elemNode)
  const { width, height } = imageInfo

  let originalX = 0
  let originalWith = 0
  let originalHeight = 0
  let revers = false // 是否反转。如向右拖拽 right-top 需增加宽度（非反转），但向右拖拽 left-top 则需要减少宽度（反转）
  let $container = null

  function getContainerElem() {
    const $container = $(`#${containerId}`)
    if ($container.length === 0) throw new Error('Cannot find image container elem')
    return $container
  }

  /**
   * 初始化。监听事件，记录原始数据
   */
  function init(clientX) {
    $container = getContainerElem()

    // 记录当前 x 坐标值
    originalX = clientX

    // 记录 img 原始宽高
    const $img = $container.find('img')
    if ($img.length === 0) throw new Error('Cannot find image elem')
    originalWith = $img.width()
    originalHeight = $img.height()

    // 监听 mousemove
    $body.on('mousemove', onMousemove)

    // 监听 mouseup
    $body.on('mouseup', onMouseup)

    // 隐藏 hoverbar
    const hoverbar = DomEditor.getHoverbar(editor)
    if (hoverbar) hoverbar.hideAndClean()
  }

  // mouseover callback （节流）
  const onMousemove = throttle((e) => {
    e.preventDefault()

    const { clientX } = e
    const gap = revers ? originalX - clientX : clientX - originalX // 考虑是否反转
    const newWidth = originalWith + gap
    const newHeight = originalHeight * (newWidth / originalWith) // 根据 width ，按比例计算 height

    // 实时修改 img 宽高 -【注意】这里只修改 DOM ，mouseup 时再统一不修改 node
    if ($container === null) return
    if (newWidth <= 15 || newHeight <= 15) return // 最小就是 15px

    $container.css('width', `${newWidth}px`)
    $container.css('height', `${newHeight}px`)
  }, 100)

  function onMouseup(e) {
    // 取消监听 mousemove
    $body.off('mousemove', onMousemove)

    if ($container === null) return
    const newWidth = $container.width().toFixed(2)
    const newHeight = $container.height().toFixed(2)

    // 修改 node
    const props = {
      style: {
        ...elemNode.style,
        width: `${newWidth}px`,
        height: `${newHeight}px`
      }
    }
    Transforms.setNodes(editor, props, { at: DomEditor.findPath(editor, elemNode) })

    // 取消监听 mouseup
    $body.off('mouseup', onMouseup)
  }

  const style = {}
  if (width) style.width = width
  if (height) style.height = height
  // style.boxShadow = '0 0 0 1px #B4D5FF' // 自定义 selected 样式，因为有拖拽触手

  const normalStyle = 'width: 7px; height: 7px; background-color: #4290f7; position: absolute; cursor: nwse-resize; z-index: 12;'

  const strDom = `${imageVnode}<div className='w-e-image-dragger left-top' style="${normalStyle} left: 0; top: 0;"></div>
  <div className='w-e-image-dragger right-top' style="${normalStyle} right: 0; top: 0;"></div>
  <div className='w-e-image-dragger left-bottom' style="${normalStyle} left: 0; bottom: 0;"></div>
  <div className='w-e-image-dragger right-bottom' style="${normalStyle} right: 0; bottom: 0;"></div>`

  return h(
    'div',
    {
      props: {
        id: containerId,
        className: 'w-e-image-container w-e-selected-image-container'
        // innerHTML: strDom,
      },
      style: style
    },
    [
      imageVnode,
      h('div', {
        props: { className: 'w-e-image-dragger left-top' },
        style: {
          width: '7px',
          height: '7px',
          backgroundColor: '#4290f7',
          position: 'absolute',
          cursor: 'nwse-resize',
          left: 0,
          top: 0
        },
        on: {
          mousedown(e) {
            const $target = $(e.target)
            if (!$target.hasClass('w-e-image-dragger')) {
              // target 不是 .w-e-image-dragger 拖拽触手，则忽略
              return
            }
            e.preventDefault()

            if ($target.hasClass('left-top') || $target.hasClass('left-bottom')) {
              revers = true // 反转。向右拖拽，减少宽度
            }
            init(e.clientX) // 初始化
          }
        }
      }),
      h('div', {
        props: { className: 'w-e-image-dragger right-top' },
        style: {
          width: '7px',
          height: '7px',
          backgroundColor: '#4290f7',
          position: 'absolute',
          cursor: 'nwse-resize',
          right: 0,
          top: 0
        },
        on: {
          mousedown(e) {
            const $target = $(e.target)
            if (!$target.hasClass('w-e-image-dragger')) {
              // target 不是 .w-e-image-dragger 拖拽触手，则忽略
              return
            }
            e.preventDefault()

            if ($target.hasClass('left-top') || $target.hasClass('left-bottom')) {
              revers = true // 反转。向右拖拽，减少宽度
            }
            init(e.clientX) // 初始化
          }
        }
      }),
      h('div', {
        props: { className: 'w-e-image-dragger left-bottom' },
        style: {
          width: '7px',
          height: '7px',
          backgroundColor: '#4290f7',
          position: 'absolute',
          cursor: 'nwse-resize',
          left: 0,
          bottom: 0
        },
        on: {
          mousedown(e) {
            const $target = $(e.target)
            if (!$target.hasClass('w-e-image-dragger')) {
              // target 不是 .w-e-image-dragger 拖拽触手，则忽略
              return
            }
            e.preventDefault()

            if ($target.hasClass('left-top') || $target.hasClass('left-bottom')) {
              revers = true // 反转。向右拖拽，减少宽度
            }
            init(e.clientX) // 初始化
          }
        }
      }),
      h('div', {
        props: { className: 'w-e-image-dragger right-bottom' },
        style: {
          width: '7px',
          height: '7px',
          backgroundColor: '#4290f7',
          position: 'absolute',
          cursor: 'nwse-resize',
          right: 0,
          bottom: 0
        },
        on: {
          mousedown(e) {
            const $target = $(e.target)
            if (!$target.hasClass('w-e-image-dragger')) {
              // target 不是 .w-e-image-dragger 拖拽触手，则忽略
              return
            }
            e.preventDefault()

            if ($target.hasClass('left-top') || $target.hasClass('left-bottom')) {
              revers = true // 反转。向右拖拽，减少宽度
            }
            init(e.clientX) // 初始化
          }
        }
      })
    ]
  )
}

// 在编辑器中渲染新元素
// 定义 renderElem 函数
const renderImage = (elem, children, editor) => {
  // 获取“附件”的数据，参考上文 myResume 数据结构
  const { title = '', imgUrl = '', imgDescript = '', style = {} } = elem
  const { width = '', height = '' } = style

  let imageStyle = ''
  if (width) imageStyle += 'width: 100%; '
  if (height) imageStyle += 'height: 100%; '

  const isDisabled = editor.isDisabled()
  const selected = DomEditor.isNodeSelected(editor, elem) // 图片是否选中

  const vnode = `<img style="${imageStyle}" src=${imgUrl} alt="${imgDescript}" data-href="${imgUrl}" />`
  let str = ''
  if (selected && !isDisabled) {
    str = renderResizeContainer(editor, elem, vnode, style)
  } else {
    str = renderImageContainer(editor, elem, vnode, style)
  }

  const imgNode = h(
    'div',
    {
      props: {
        className: 'chapter-image-pic'
      },
      style: {
        position: 'relative',
        fontSize: '0'
      }
    },
    [str]
  )
  const titleNode = h(
    'p',
    {
      props: {
        className: 'chapter-image-title'
      },
      style: { color: '#666', fontSize: '14px', margin: '0', lineHeight: '20px' }
    },
    [title]
  )
  const titleDescriptNode = h(
    'p',
    {
      props: {
        className: 'chapter-image-descript'
      },
      style: { color: '#999', fontSize: '14px', margin: '0', lineHeight: '20px' }
    },
    [imgDescript]
  )

  const element = [imgNode]
  if (title) {
    // element.push(titleNode);
  }
  if (imgDescript) {
    // element.push(titleDescriptNode);
  }

  // 附件元素 vnode
  const attachVnode = h(
    // HTML tag
    'span',
    // HTML 属性、样式、事件
    {
      props: {
        // HTML 属性，驼峰式写法
        contentEditable: false,
        className: 'chapter-image-container'
      },
      style: { padding: '10px 0px' }, // style ，驼峰式写法
      on: {
        click() {
          console.log('clicked')
        } /* 其他... */
      }
    },
    // 子节点
    [...element]
  )

  return attachVnode
}
const renderElemConf = {
  type: 'chapterImage',
  renderElem: renderImage
}

// 把新元素转换为 HTML
const chapterImageToHtml = (elem, childrenHtml) => {
  // 获取附件元素的数据
  const { title = '', imgUrl = '', imgDescript, style = {} } = elem
  const { width = '', height = '' } = style

  let styleStr = ''
  if (width) styleStr += `width: ${width};`
  if (height) styleStr += `height: ${height};`

  // 生成 HTML 代码
  const html = `<span
    class="chapter-image-container"
    data-w-e-type="chapterImage"
    data-w-e-is-void
    data-imgDescript="${imgDescript}"
    data-imgUrl="${imgUrl}"
    data-title="${title}"
  >
    <div class="chapter-image-pic" style="position: relative; font-size: 0px;">
      <img src="${imgUrl}" alt="${imgDescript}" style="${styleStr}" />
    </div>
  </span>`

  // `<p class="chapter-image-title">${title}</p><p class="chapter-image-descript">${imgDescript}</p>`

  return html
}
const chapterImageElemToHtmlConf = {
  type: 'chapterImage', // 新元素的 type ，重要！！！
  elemToHtml: chapterImageToHtml
}

// 解析新元素 HTML 到编辑器
const parseImageHtml = (domElem, children, editor) => {
  // 从 DOM element 中获取“附件”的信息
  const imgUrl = domElem.getAttribute('data-imgUrl') || ''
  const imgDescript = domElem.getAttribute('data-imgDescript') || ''
  const title = domElem.getAttribute('data-title') || ''
  const $elem = $(domElem)

  // 生成“附件”元素（按照此前约定的数据结构）
  const myResume = {
    type: 'chapterImage',
    title,
    imgUrl,
    imgDescript,
    style: {
      width: getStyleValue($elem, 'width'),
      height: getStyleValue($elem, 'height')
    },
    children: [{ text: '' }] // void node 必须有 children ，其中有一个空字符串，重要！！！
  }

  return myResume
}
const parseImageConf = {
  selector: 'span[data-w-e-type="chapterImage"]', // CSS 选择器，匹配特定的 HTML 标签
  parseElemHtml: parseImageHtml
}

const chapterImageModule = {
  editorPlugin: withImageNode,
  renderElems: [renderElemConf],
  elemsToHtml: [chapterImageElemToHtmlConf],
  parseElemsHtml: [parseImageConf],
  menus: [ImageAutoConf, imageWidth100MenuChapterConf, imageWidth50MenuChapterConf, imageWidth30MenuChapterConf]
}

export default chapterImageModule
export { withImageNode, renderElemConf, chapterImageElemToHtmlConf, parseImageConf }
