import type { Schema } from 'prosemirror-model'
import { type Transaction, TextSelection, AllSelection } from 'prosemirror-state'
import type { EditorView } from 'prosemirror-view'
import { isList } from './toggleList'

type IndentKey = 'indent' | 'textIndent'

function setNodeIndentMarkup(tr: Transaction, pos: number, delta: number, indentKey: IndentKey): Transaction {
  if (!tr.doc) return tr

  const node = tr.doc.nodeAt(pos)
  if (!node) return tr

  const minIndent = 0
  const maxIndent = 8

  let indent = (node.attrs[indentKey] || 0) + delta
  if (indent < minIndent) indent = minIndent
  if (indent > maxIndent) indent = maxIndent

  if (indent === node.attrs[indentKey]) return tr

  const nodeAttrs = {
    ...node.attrs,
    [indentKey]: indent,
  }

  return tr.setNodeMarkup(pos, node.type, nodeAttrs, node.marks)
}

const setIndent = (tr: Transaction, schema: Schema, delta: number, indentKey: IndentKey): Transaction => {
  const { selection, doc } = tr
  if (!selection || !doc) return tr

  if (!(selection instanceof TextSelection || selection instanceof AllSelection)) return tr

  const { from, to } = selection

  doc.nodesBetween(from, to, (node, pos) => {
    const nodeType = node.type

    if (nodeType.name === 'paragraph' || nodeType.name === 'blockquote') {
      tr = setNodeIndentMarkup(tr, pos, delta, indentKey)
      return false
    } 
    else if (isList(node, schema)) return false
    return true
  })

  return tr
}

export const indentCommand = (view: EditorView, delta: number) => {
  const { state } = view
  const { schema, selection } = state

  const tr = setIndent(
    state.tr.setSelection(selection),
    schema,
    delta,
    'indent',
  )
  if (tr.docChanged) {
    view.dispatch(tr)
    return true
  }

  return false
}

export const textIndentCommand = (view: EditorView, delta: number) => {
  const { state } = view
  const { schema, selection } = state

  const tr = setIndent(
    state.tr.setSelection(selection),
    schema,
    delta,
    'textIndent',
  )
  if (tr.docChanged) {
    view.dispatch(tr)
    return true
  }

  return false
}