require "../../lsp/protocol"

module Lattice
  module Completion
    # CompletionProvider is the abstract base for all completion sources.
    # Each provider receives the document URI, source text, and cursor position
    # and returns zero or more `LSP::CompletionItem` values.
    #
    # Providers are collected by `SemanticAnalyzer#completions` and their results
    # are merged and de-duplicated before being returned to the client.
    abstract class CompletionProvider
      # Returns completion items for the given position in *source*.
      abstract def completions(
        uri : String,
        source : String,
        position : ::LSP::Position,
      ) : Array(::LSP::CompletionItem)

      # Convenience: build a keyword-kind item.
      protected def keyword_item(label : String) : ::LSP::CompletionItem
        item = ::LSP::CompletionItem.new(label)
        item.kind = ::LSP::CompletionItemKind::Keyword
        item
      end

      # Convenience: build a method-kind item with optional detail.
      protected def method_item(label : String, detail : String? = nil) : ::LSP::CompletionItem
        item = ::LSP::CompletionItem.new(label)
        item.kind = ::LSP::CompletionItemKind::Method
        item.detail = detail if detail
        item
      end

      # Convenience: build a class-kind item.
      protected def class_item(label : String, detail : String? = nil) : ::LSP::CompletionItem
        item = ::LSP::CompletionItem.new(label)
        item.kind = ::LSP::CompletionItemKind::Class
        item.detail = detail if detail
        item
      end

      # Convenience: build a variable-kind item.
      protected def variable_item(label : String, detail : String? = nil) : ::LSP::CompletionItem
        item = ::LSP::CompletionItem.new(label)
        item.kind = ::LSP::CompletionItemKind::Variable
        item.detail = detail if detail
        item
      end

      # Convenience: build a field-kind item.
      protected def field_item(label : String, detail : String? = nil) : ::LSP::CompletionItem
        item = ::LSP::CompletionItem.new(label)
        item.kind = ::LSP::CompletionItemKind::Field
        item.detail = detail if detail
        item
      end

      # Extract the word (identifier fragment) immediately before *position*.
      protected def word_before(source : String, position : ::LSP::Position) : String
        lines = source.split('\n')
        return "" if position.line >= lines.size
        line = lines[position.line]
        col = position.character.clamp(0, line.size)
        start = col
        while start > 0 && word_char?(line[start - 1])
          start -= 1
        end
        line[start...col]
      end

      private def word_char?(c : Char) : Bool
        c.alphanumeric? || c == '_' || c == '?' || c == '!'
      end
    end
  end
end
