import { Diagnostic, DiagnosticSeverity, Range } from 'vscode-languageserver/node'
import { SyntaxNode } from 'web-tree-sitter'

/**
 * Recursively iterate over all nodes in a tree.
 *
 * @param node The node to start iterating from
 * @param callback The callback to call for each node. Return false to stop following children.
 */
export function forEach(node: SyntaxNode, callback: (n: SyntaxNode) => void | boolean) {
  const followChildren = callback(node) !== false
  if (followChildren && node.children.length) {
    node.children.forEach((n) => forEach(n, callback))
  }
}

export function range(n: SyntaxNode): Range {
  return Range.create(
    n.startPosition.row,
    n.startPosition.column,
    n.endPosition.row,
    n.endPosition.column,
  )
}

export function isDefinition(n: SyntaxNode): boolean {
  switch (n.type) {
    case 'variable_assignment':
    case 'function_definition':
      return true
    default:
      return false
  }
}

export function isReference(n: SyntaxNode): boolean {
  switch (n.type) {
    case 'variable_name':
    case 'command_name':
      return true
    default:
      return false
  }
}

export function findParent(
  start: SyntaxNode,
  predicate: (n: SyntaxNode) => boolean,
): SyntaxNode | null {
  let node = start.parent
  while (node !== null) {
    if (predicate(node)) {
      return node
    }
    node = node.parent
  }
  return null
}

export function getDiagnosticsForMissingNodes(node: SyntaxNode) {
  const diagnostics: Diagnostic[] = []

  forEach(node, (node) => {
    if (node.isMissing()) {
      diagnostics.push(
        Diagnostic.create(
          range(node),
          `Syntax error: "${node.type}" missing`,
          DiagnosticSeverity.Warning,
          undefined,
          'bash-language-server',
        ),
      )
    }
    return node.hasError()
  })

  return diagnostics
}
