import { InputRule } from 'prosemirror-inputrules'
import { EditorState } from 'prosemirror-state'
import { MarkType } from 'prosemirror-model'

function getMarksBetween(start: number, end: number, state: EditorState) {
  let marks: any[] = []

  state.doc.nodesBetween(start, end, (node, pos) => {
    marks = [...marks, ...node.marks.map(mark => ({
      start: pos,
      end: pos + node.nodeSize,
      mark,
    }))]
  })

  return marks
}

export default function (regexp: RegExp, markType: MarkType, getAttrs?: Function) {
  return new InputRule(regexp, (state, match, start, end) => {
    const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs
    const { tr } = state
    const m = match.length - 1
    let markEnd = end
    let markStart = start

    if (match[m]) {
      const matchStart = start + match[0].indexOf(match[m - 1])
      const matchEnd = matchStart + match[m - 1].length
      const textStart = matchStart + match[m - 1].lastIndexOf(match[m])
      const textEnd = textStart + match[m].length

      const excludedMarks = getMarksBetween(start, end, state)
        .filter(item => {
          const { excluded } = item.mark.type
          return excluded.find((type: MarkType) => type.name === markType.name)
        })
        .filter(item => item.end > matchStart)

      if (excludedMarks.length) {
        return null
      }

      if (textEnd < matchEnd) {
        tr.delete(textEnd, matchEnd)
      }

      if (textStart > matchStart) {
        tr.delete(matchStart, textStart)
      }

      markStart = matchStart
      markEnd = markStart + match[m].length
    }

    tr.addMark(markStart, markEnd, markType.create(attrs))
    tr.removeStoredMark(markType)
    return tr
  })
}
