// Document symbols and go-to-definition provider for the ƿit LSP.

// SymbolKind constants (LSP spec)
def KIND_FUNCTION = 12
def KIND_VARIABLE = 13
def KIND_CONSTANT = 14

// Walk AST to extract document symbols (top-level vars/defs and functions).
var document_symbols = function(doc) {
  var symbols = []
  var ast = doc.ast
  var _i = 0
  var _j = 0
  var stmt = null
  var decl = null
  var name = null
  var kind = null
  var range = null

  if (ast == null || ast.statements == null) {
    return symbols
  }

  while (_i < length(ast.statements)) {
    stmt = ast.statements[_i]

    if (stmt.kind == "var" || stmt.kind == "def") {
      name = null
      kind = KIND_VARIABLE

      if (stmt.left != null && stmt.left.name != null) {
        name = stmt.left.name
      }

      if (stmt.kind == "def") {
        kind = KIND_CONSTANT
      }

      if (stmt.right != null && (stmt.right.kind == "function" || stmt.right.kind == "arrow function")) {
        kind = KIND_FUNCTION
      }

      if (name != null) {
        range = {
          start: {line: stmt.from_row, character: stmt.from_column},
          end: {line: stmt.to_row, character: stmt.to_column}
        }
        symbols[] = {
          name: name,
          kind: kind,
          range: range,
          selectionRange: {
            start: {line: stmt.left.from_row, character: stmt.left.from_column},
            end: {line: stmt.left.to_row, character: stmt.left.to_column}
          }
        }
      }
    }

    if (stmt.kind == "var_list" && stmt.list != null) {
      _j = 0
      while (_j < length(stmt.list)) {
        decl = stmt.list[_j]
        if (decl.left != null && decl.left.name != null) {
          kind = (decl.kind == "def") ? KIND_CONSTANT : KIND_VARIABLE
          if (decl.right != null && (decl.right.kind == "function" || decl.right.kind == "arrow function")) {
            kind = KIND_FUNCTION
          }
          range = {
            start: {line: decl.from_row, character: decl.from_column},
            end: {line: decl.to_row, character: decl.to_column}
          }
          symbols[] = {
            name: decl.left.name,
            kind: kind,
            range: range,
            selectionRange: {
              start: {line: decl.left.from_row, character: decl.left.from_column},
              end: {line: decl.left.to_row, character: decl.left.to_column}
            }
          }
        }
        _j = _j + 1
      }
    }

    _i = _i + 1
  }

  return symbols
}

// Find the declaration location of a name at a given position.
// Uses the semantic index when available, falls back to AST walk.
var definition = function(doc, line, col, token_at) {
  var tok = token_at(doc, line, col)
  var name = null
  var _i = 0
  var sym = null
  var decl = null

  if (tok == null || tok.kind != "name" || tok.value == null) {
    return null
  }

  name = tok.value

  // Use the semantic index if available
  if (doc.index != null) {
    _i = 0
    while (_i < length(doc.index.symbols)) {
      sym = doc.index.symbols[_i]
      if (sym.name == name && sym.decl_span != null) {
        return {
          uri: doc.uri,
          range: {
            start: {line: sym.decl_span.from_row, character: sym.decl_span.from_col},
            end: {line: sym.decl_span.to_row, character: sym.decl_span.to_col}
          }
        }
      }
      _i = _i + 1
    }
  }

  // Fallback: walk statements for var/def with this name
  if (doc.ast != null) {
    decl = find_declaration(doc.ast.statements, name)
    if (decl != null) {
      return {
        uri: doc.uri,
        range: {
          start: {line: decl.from_row, character: decl.from_column},
          end: {line: decl.to_row, character: decl.to_column}
        }
      }
    }
  }

  return null
}

// Recursively search statements for a var/def declaration of a given name.
var find_declaration = function(statements, name) {
  var _i = 0
  var _j = 0
  var stmt = null
  var result = null

  if (statements == null) {
    return null
  }

  while (_i < length(statements)) {
    stmt = statements[_i]

    // Direct var/def
    if ((stmt.kind == "var" || stmt.kind == "def")
        && stmt.left != null && stmt.left.name == name) {
      return stmt
    }

    // var_list
    if (stmt.kind == "var_list" && stmt.list != null) {
      _j = 0
      while (_j < length(stmt.list)) {
        if (stmt.list[_j].left != null && stmt.list[_j].left.name == name) {
          return stmt.list[_j]
        }
        _j = _j + 1
      }
    }

    // Recurse into blocks
    if (stmt.statements != null) {
      result = find_declaration(stmt.statements, name)
      if (result != null) {
        return result
      }
    }

    // if/else
    if (stmt.kind == "if") {
      if (stmt.then != null && stmt.then.statements != null) {
        result = find_declaration(stmt.then.statements, name)
        if (result != null) {
          return result
        }
      }
      if (stmt.else != null && stmt.else.statements != null) {
        result = find_declaration(stmt.else.statements, name)
        if (result != null) {
          return result
        }
      }
    }

    // Function body
    if ((stmt.kind == "function" || stmt.kind == "arrow function") && stmt.statements != null) {
      result = find_declaration(stmt.statements, name)
      if (result != null) {
        return result
      }
    }

    // var/def with function right side
    if ((stmt.kind == "var" || stmt.kind == "def") && stmt.right != null) {
      if ((stmt.right.kind == "function" || stmt.right.kind == "arrow function") && stmt.right.statements != null) {
        result = find_declaration(stmt.right.statements, name)
        if (result != null) {
          return result
        }
      }
    }

    _i = _i + 1
  }
  return null
}

return {
  document_symbols: document_symbols,
  definition: definition
}
