import React, {useEffect, useMemo, useRef, useState} from 'react'
import {withReact, Slate, ReactEditor} from '5e-slate-react'
import {createEditor, Descendant, Range, Editor, Transforms} from 'slate'
import {withHistory} from 'slate-history'

import {EditorProvider} from './editor_context'
import {EditorType} from '../global/interface'
import {withMentions} from '../plugins/withMentions'
import MentionsItem from '../element/mentions/portal_item'
import {getInitialValue, initialValue} from '../global/utils'
import EditorWrapper from './editor'
import {initParagraph} from '../global/const'
import {insertMention} from '../global/tool'
import {withCompletion} from '../plugins/withCompletion'

export interface OperateNodes {
  type: 'insert' | 'empty'
  nodes: any[] | any
  initData?: InitData<any>[]
  isReply?: boolean
}

interface InitData<T> {
  type: string
  children: {text: string}[] | InitData<T>[]
  character?: T
}

// TODO: any 类型修改
interface Props {
  // readonly 编辑器是否
  readonly: boolean
  // value
  data?: InitData<any>[]
  // selectArr 可以被选中的人
  selectArr?: any[]
  // insetNodes (外部插入节点: 重置 + 插入)
  operateNodes?: OperateNodes
  // className
  classNames?: string
  // mentionPosition
  mentionPosition?: {
    top?: boolean
    right?: boolean
  }
  // isHideMention 隐藏mention
  isHideMention?: boolean

  // changeValue
  changeValue?(s: any): void

  // search 开始搜索
  onSearch?(s: string): void

  onResetValue?(cb: any): void
  // 子组件传递父组件的回调函数
  onCallBack?(...s: any): void
}
const BaseEditor = (baseProps: Partial<Props> = {onSearch: () => void 0}) => {
  const {
    readonly,
    changeValue,
    data,
    operateNodes,
    selectArr,
    mentionPosition,
    classNames,
    isHideMention,
    onCallBack = () => void 0,
  } = baseProps
  const [value, setValue] = useState<Descendant[]>(initialValue(''))

  const [target, setTarget] = useState<Range | undefined>()
  const [index, setIndex] = useState(0)
  const [search, setSearch] = useState('')
  const timer = useRef<any>(null)

  // 初始化默认值
  useEffect(() => {
    if (!data?.length) return
    setValue(data)
    // TODO： 默认进入时设置好内容后 -> 默认让其失去焦点
  }, [data])

  // 失去焦点
  const onBlur = () => ReactEditor.blur(editor)
  // 聚焦
  const onFocus = () => ReactEditor.focus(editor)

  const reSetValue = () => {
    Transforms.select(editor, Editor.start(editor, []))
    setValue(initParagraph)
  }

  useEffect(() => {
    // eslint-disable-next-line no-unused-expressions
    baseProps?.onResetValue?.(reSetValue)
  }, [])

  // editor 编辑器(相关)
  const editor = useMemo<EditorType>(
    () => withCompletion(withMentions(withHistory(withReact(createEditor() as ReactEditor)))),
    []
  )
  // 组件函数直接抛出外层函数进行执行
  useEffect(() => {
    onCallBack({
      onBlur: () => ReactEditor.blur(editor),
      onFocus: () => ReactEditor.focus(editor),
      insertData: insertInfo(editor),
    })
  }, [editor])
  // 外部插入数据时 触发的逻辑
  useEffect(() => {
    if (operateNodes?.type === 'insert') {
      setValue(operateNodes.initData || [])
      timer.current = setTimeout(() => {
        ReactEditor.focus(editor)
        const mention: any = getInitialValue('回复', operateNodes)
        Transforms.insertNodes(editor, mention)
        Transforms.move(editor)
        clearTimeout(timer.current)
      }, 100)
    }
  }, [operateNodes])

  // 插入数据
  const insertInfo = (editors: any) => (info: any) => {
    ReactEditor.focus(editors)
    Transforms.collapse(editors, {edge: 'anchor'})
    // console.error(editors.selection, '=')
    Transforms.insertNodes(editors, info, {at: editor.selection?.anchor})
    Transforms.move(editors)
  }

  const changeValues = (v: any) => {
    if (readonly && !isHideMention) return
    if (JSON.stringify(v) === JSON.stringify(value)) return
    setValue(v)
    changeValue!(v)
    const {selection} = editor
    // console.error(editor.selection, '===')

    // 只出现@时单独处理逻辑
    if (sessionStorage.getItem('isFirstMention')) {
      const [start] = Range.edges(selection!)
      const wordBefore = Editor.before(editor, start, {unit: 'offset'})
      const afterRange = wordBefore && Editor.range(editor, wordBefore, start)
      // console.error(afterRange, 'afterRange')
      if (afterRange) {
        setTarget(afterRange)
        baseProps.onSearch!('')
        setIndex(0)
        sessionStorage.setItem('isFirstMention', '')
      }
      return
    }
    // @后面接文字
    if (selection && Range.isCollapsed(selection)) {
      const [start] = Range.edges(selection)
      const wordBefore = Editor.before(editor, start, {unit: 'word'})
      const before = wordBefore && Editor.before(editor, wordBefore)
      const beforeRange = before && Editor.range(editor, before, start)
      const beforeText = beforeRange && Editor.string(editor, beforeRange)
      // 支持@ 后面传入中文
      const beforeMatch = beforeText && beforeText?.match(/^@(.+)$/)
      if (beforeMatch) {
        setTarget(beforeRange)
        setSearch(beforeMatch[1])
        baseProps.onSearch!(beforeMatch[1])
        setIndex(0)
        return
      }
    }
    setTarget(undefined)
  }

  // 鼠标点击事件
  const onClickItem = (i: number) => {
    Transforms.select(editor, target!)
    insertMention(editor, chars[i])
    Transforms.insertText(editor, ' ')
    ReactEditor.focus(editor)
    setTarget(undefined)
  }

  // 键盘输入 特殊符号时 触发相应的逻辑
  const chars = selectArr?.slice(0, 10) || []
  console.error(value, '----')
  return (
    <EditorProvider editor={editor}>
      <Slate editor={editor} onChange={changeValues} value={value}>
        <div>
          <EditorWrapper
            mentionProps={{index, search, target, setIndex, setTarget, chars, classNames}}
            readonly={readonly!}
          />
          {/* 移动端屏蔽@ 逻辑 */}
          {target && !isHideMention && chars.length > 0 && (
            <MentionsItem
              chars={chars}
              clickItem={onClickItem}
              editor={editor}
              index={index}
              mentionPosition={mentionPosition}
              search={search}
              target={target}
            />
          )}
        </div>
      </Slate>
    </EditorProvider>
  )
}

// 数据格

export default BaseEditor
