import { checkboxRegex } from "./todo"
import { EditorView } from "@codemirror/view"

export const autoTodoOnNewline = EditorView.updateListener.of((update) => {
  if (update.state.selection.ranges.length > 1) return

  for (const tr of update.transactions) {
    if (!tr.isUserEvent("input")) return

    tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
      handleNewlineChange({ update, fromA, toA, fromB, toB, inserted })
    })
  }
})

type HandleNewlineChangeOpts = {
  update: any
  fromA: number
  toA: number
  fromB: number
  toB: number
  inserted: any
}

const handleNewlineChange = (opts: HandleNewlineChangeOpts) => {
  if (!opts.inserted.toString().match(/\s*\n/)) return

  const insertPos = opts.fromB + opts.inserted.toString().length
  const prevLine = opts.update.state.doc.lineAt(insertPos - opts.inserted.toString().length)

  if (opts.inserted.toString().trim().length > 0) {
    // If the inserted text is not just a newline, we don't want to auto-add a checkbox
    return
  } else if (prevLine.text.trim() === "") {
    // If the previous line is empty, we don't want to add a checkbox
    return
  } else if (endsWith(prevLine.text, checkboxRegex)) {
    // If the previous line has an empty checkbox, we don't want to add a new one
    const start = prevLine.from
    const end = insertPos
    opts.update.view.dispatch({
      changes: { from: start, to: end, insert: "" },
      selection: { anchor: start },
      userEvent: "delete",
    })
  } else {
    const prefix = `[ ] `
    opts.update.view.dispatch({
      changes: { from: insertPos, to: insertPos, insert: prefix },
      selection: { anchor: insertPos + prefix.length },
      userEvent: "todo.newline",
    })
  }
}

const endsWith = (str: string, regex: RegExp) => {
  const match = str.match(regex)
  return match && match.index === str.length - match[0].length
}
