class RubyLsp::Listeners::Completion

def build_completion(label, node)

def build_completion(label, node)
  # We should use the content location as we only replace the content and not the delimiters of the string
  loc = node.content_loc
  Interface::CompletionItem.new(
    label: label,
    text_edit: Interface::TextEdit.new(
      range: range_from_location(loc),
      new_text: label,
    ),
    kind: Constant::CompletionItemKind::FILE,
  )
end

def build_entry_completion(real_name, incomplete_name, node, entries, top_level)

def build_entry_completion(real_name, incomplete_name, node, entries, top_level)
  first_entry = T.must(entries.first)
  kind = case first_entry
  when RubyIndexer::Entry::Class
    Constant::CompletionItemKind::CLASS
  when RubyIndexer::Entry::Module
    Constant::CompletionItemKind::MODULE
  when RubyIndexer::Entry::Constant
    Constant::CompletionItemKind::CONSTANT
  else
    Constant::CompletionItemKind::REFERENCE
  end
  insertion_text = real_name.dup
  filter_text = real_name.dup
  # If we have two entries with the same name inside the current namespace and the user selects the top level
  # option, we have to ensure it's prefixed with `::` or else we're completing the wrong constant. For example:
  # If we have the index with ["Foo::Bar", "Bar"], and we're providing suggestions for `B` inside a `Foo` module,
  # then selecting the `Foo::Bar` option needs to complete to `Bar` and selecting the top level `Bar` option needs
  # to complete to `::Bar`.
  if top_level
    insertion_text.prepend("::")
    filter_text.prepend("::")
  end
  # If the user is searching for a constant inside the current namespace, then we prefer completing the short name
  # of that constant. E.g.:
  #
  # module Foo
  #  class Bar
  #  end
  #
  #  Foo::B # --> completion inserts `Bar` instead of `Foo::Bar`
  # end
  @nesting.each do |namespace|
    prefix = "#{namespace}::"
    shortened_name = insertion_text.delete_prefix(prefix)
    # If a different entry exists for the shortened name, then there's a conflict and we should not shorten it
    conflict_name = "#{@nesting.join("::")}::#{shortened_name}"
    break if real_name != conflict_name && @index[conflict_name]
    insertion_text = shortened_name
    # If the user is typing a fully qualified name `Foo::Bar::Baz`, then we should not use the short name (e.g.:
    # `Baz`) as filtering. So we only shorten the filter text if the user is not including the namespaces in their
    # typing
    filter_text.delete_prefix!(prefix) unless incomplete_name.start_with?(prefix)
  end
  # When using a top level constant reference (e.g.: `::Bar`), the editor includes the `::` as part of the filter.
  # For these top level references, we need to include the `::` as part of the filter text or else it won't match
  # the right entries in the index
  Interface::CompletionItem.new(
    label: real_name,
    filter_text: filter_text,
    text_edit: Interface::TextEdit.new(
      range: range_from_node(node),
      new_text: insertion_text,
    ),
    kind: kind,
    label_details: Interface::CompletionItemLabelDetails.new(
      description: entries.map(&:file_name).join(","),
    ),
    documentation: Interface::MarkupContent.new(
      kind: "markdown",
      value: markdown_from_index_entries(real_name, entries),
    ),
  )
end

def build_method_completion(entry, node)

def build_method_completion(entry, node)
  name = entry.name
  Interface::CompletionItem.new(
    label: name,
    filter_text: name,
    text_edit: Interface::TextEdit.new(range: range_from_node(node), new_text: name),
    kind: Constant::CompletionItemKind::METHOD,
    label_details: Interface::CompletionItemLabelDetails.new(
      detail: "(#{entry.parameters.map(&:decorated_name).join(", ")})",
      description: entry.file_name,
    ),
    documentation: Interface::MarkupContent.new(
      kind: "markdown",
      value: markdown_from_index_entries(name, entry),
    ),
  )
end

def initialize(response_builder, index, nesting, typechecker_enabled, dispatcher)

def initialize(response_builder, index, nesting, typechecker_enabled, dispatcher)
  @response_builder = response_builder
  @index = index
  @nesting = nesting
  @typechecker_enabled = typechecker_enabled
  dispatcher.register(
    self,
    :on_string_node_enter,
    :on_constant_path_node_enter,
    :on_constant_read_node_enter,
    :on_call_node_enter,
  )
end

def on_call_node_enter(node)

def on_call_node_enter(node)
  return if @typechecker_enabled
  return unless self_receiver?(node)
  name = node.message
  return unless name
  receiver_entries = @index[@nesting.join("::")]
  return unless receiver_entries
  receiver = T.must(receiver_entries.first)
  @index.prefix_search(name).each do |entries|
    entry = entries.find { |e| e.is_a?(RubyIndexer::Entry::Member) && e.owner&.name == receiver.name }
    next unless entry
    @response_builder << build_method_completion(T.cast(entry, RubyIndexer::Entry::Member), node)
  end
end

def on_constant_path_node_enter(node)

def on_constant_path_node_enter(node)
  return if DependencyDetector.instance.typechecker
  name = node.slice
  top_level_reference = if name.start_with?("::")
    name = name.delete_prefix("::")
    true
  else
    false
  end
  # If we're trying to provide completion for an aliased namespace, we need to first discover it's real name in
  # order to find which possible constants match the desired search
  *namespace, incomplete_name = name.split("::")
  aliased_namespace = T.must(namespace).join("::")
  namespace_entries = @index.resolve(aliased_namespace, @nesting)
  return unless namespace_entries
  real_namespace = @index.follow_aliased_namespace(T.must(namespace_entries.first).name)
  candidates = @index.prefix_search("#{real_namespace}::#{incomplete_name}", top_level_reference ? [] : @nesting)
  candidates.each do |entries|
    # The only time we may have a private constant reference from outside of the namespace is if we're dealing
    # with ConstantPath and the entry name doesn't start with the current nesting
    first_entry = T.must(entries.first)
    next if first_entry.visibility == :private && !first_entry.name.start_with?("#{@nesting}::")
    constant_name = T.must(first_entry.name.split("::").last)
    full_name = aliased_namespace.empty? ? constant_name : "#{aliased_namespace}::#{constant_name}"
    @response_builder << build_entry_completion(
      full_name,
      name,
      node,
      entries,
      top_level_reference || top_level?(T.must(entries.first).name),
    )
  end
end

def on_constant_read_node_enter(node)

def on_constant_read_node_enter(node)
  return if DependencyDetector.instance.typechecker
  name = node.slice
  candidates = @index.prefix_search(name, @nesting)
  candidates.each do |entries|
    complete_name = T.must(entries.first).name
    @response_builder << build_entry_completion(
      complete_name,
      name,
      node,
      entries,
      top_level?(complete_name),
    )
  end
end

def on_string_node_enter(node)

def on_string_node_enter(node)
  @index.search_require_paths(node.content).map!(&:require_path).sort!.each do |path|
    @response_builder << build_completion(T.must(path), node)
  end
end

def top_level?(entry_name)

def top_level?(entry_name)
  @nesting.length.downto(0).each do |i|
    prefix = T.must(@nesting[0...i]).join("::")
    full_name = prefix.empty? ? entry_name : "#{prefix}::#{entry_name}"
    next if full_name == entry_name
    return true if @index[full_name]
  end
  false
end