import { commands } from "./commands"
import { cmdPrompt, cmdHint, cmdSuggestionList, cmdError } from "./dom"
import { ParseError } from "./errors"

let tabCompletion = ""

cmdPrompt.addEventListener("keydown", (e) => {
  if (e.key === "Enter") {
    console.log("ENTER")
    e.preventDefault
  } else if (e.key === "Tab") {
    e.preventDefault()
    cmdPrompt.value = tabCompletion
    updateSuggestionList(cmdPrompt.value)
  }
})

cmdPrompt.addEventListener("input", (e) => {
  updateSuggestionList(cmdPrompt.value)
})

// Corey hates this
const updateSuggestionList = (input: string) => {
  if (input.trim() == "") return

  cmdSuggestionList.innerHTML = ""
  cmdError.innerHTML = ""
  tabCompletion = ""

  const { commandSuggestions, argSuggestions, error } = getSuggestions(input)

  if (error) {
    const beforeError = input.slice(0, error.start)
    const errorText = input.slice(error.start, error.end)
    const afterError = input.slice(error.end)
    cmdError.innerHTML = `${beforeError}<span class='error'>${errorText}</span>${afterError}`
  }

  // Show command suggestions
  commandSuggestions.forEach((cmdShape) => {
    const div = createSuggestionElement(cmdShape.command, cmdShape.description)
    cmdSuggestionList.appendChild(div)
  })

  // Show arg suggestions
  argSuggestions?.forEach((arg) => {
    let name = arg.name
    if (arg.named) {
      name = `${name}=${arg.default}`
    }
    const description = `<${arg.type}>` + (arg.description ? ` - ${arg.description}` : "")
    const div = createSuggestionElement(name, description)
    cmdSuggestionList.appendChild(div)
  })

  cmdHint.value = ""
  tabCompletion = ""

  // Tab completion
  if (commandSuggestions.length > 0) {
    cmdHint.value = commandSuggestions[0]!.command
    tabCompletion = cmdHint.value + " "
  } else if (argSuggestions?.length > 0) {
    const suggestion = argSuggestions[0]!

    const lastArg = input.trimStart().split(" ").pop() ?? ""
    if (suggestion?.named) {
      if (lastArg.match(/^[\w-]+=/)) {
        const afterEquals = lastArg.split("=")[1] ?? ""
        if (!afterEquals) {
          const suggestionText = `<${suggestion.type}>`
          tabCompletion = input + suggestionText
          cmdHint.value = tabCompletion
        }
      } else {
        const suggestionText = suggestion.name.slice(lastArg.length) + "="
        tabCompletion = input + suggestionText
        cmdHint.value = tabCompletion
      }
    } else if (!lastArg) {
      tabCompletion = ""
      cmdHint.value = input + suggestion?.name + " "
    }
  }
}

const getSuggestions = (input: string) => {
  try {
    const commandInput = input.trimStart().split(" ")[0] ?? ""
    const matchingCommands = commands.filter((cmd) => cmd.shape.command.startsWith(commandInput))
    const hasCommand = input.trimStart().match(/\w+\s+/)

    if (!hasCommand) {
      return {
        commandSuggestions: matchingCommands.map((cmd) => cmd.shape),
        argSuggestions: [],
        command: null,
      }
    } else if (matchingCommands.length === 1) {
      const command = matchingCommands[0]!
      const { suggestions } = command.getSuggestions(input)
      return {
        commandSuggestions: [],
        argSuggestions: suggestions,
      }
    } else {
      return {
        commandSuggestions: [],
        argSuggestions: [],
        error: new ParseError(`Unknown command: "${commandInput}."`, 0, commandInput.length),
      }
    }
  } catch (error) {
    if (error instanceof ParseError) {
      return { commandSuggestions: [], argSuggestions: [], error }
    }
    throw error
  }
}

const createSuggestionElement = (title: string, description: string) => {
  const div = document.createElement("div")
  div.className = "command-suggestion"
  const commandDiv = document.createElement("div")
  commandDiv.textContent = title
  commandDiv.className = "command-name"
  const descriptionDiv = document.createElement("div")
  descriptionDiv.textContent = description
  descriptionDiv.className = "command-description"
  div.appendChild(commandDiv)
  div.appendChild(descriptionDiv)
  return div
}
