require "digest/sha256"
require "../../lsp/protocol"

module Lattice
  module Index
    # SymbolEntry is a lightweight, serialization-friendly record describing a
    # single named symbol (class, module, method, constant, etc.) discovered
    # during indexing of a single source file.
    struct SymbolEntry
      include JSON::Serializable

      property name : String
      property kind : String      # "class" | "module" | "struct" | "method" | "constant" | …
      property type_name : String # inferred or declared return/parent type
      property file : String      # absolute file-system path
      property line : Int32       # 0-based line number
      property column : Int32     # 0-based column
      property signature : String?
      property documentation : String?

      def initialize(
        @name : String,
        @kind : String,
        @type_name : String,
        @file : String,
        @line : Int32,
        @column : Int32 = 0,
        @signature : String? = nil,
        @documentation : String? = nil,
      )
      end

      # Convenience: return an LSP `Location` for this entry.
      def to_location : LSP::Location
        pos = LSP::Position.new(@line, @column)
        rng = LSP::Range.new(pos, pos)
        LSP::Location.new("file://#{@file}", rng)
      end
    end

    # FileIndex holds every `SymbolEntry` extracted from a single source file.
    # It is keyed by the absolute file-system path and stores a CRC-32 hash of
    # the source at index time so callers can skip re-indexing unchanged files.
    class FileIndex
      include JSON::Serializable

      getter path : String
      getter source_hash : UInt32
      getter symbols : Array(SymbolEntry)
      getter indexed_at : String  # ISO-8601 timestamp

      def initialize(@path : String, @source_hash : UInt32, @symbols : Array(SymbolEntry))
        @indexed_at = Time.utc.to_rfc3339
      end

      # Returns all symbols whose `kind` matches the given value.
      def symbols_of_kind(kind : String) : Array(SymbolEntry)
        @symbols.select { |s| s.kind == kind }
      end

      # Returns all symbols whose `name` exactly matches the given value.
      def find_by_name(name : String) : Array(SymbolEntry)
        @symbols.select { |s| s.name == name }
      end

      # Cheap staleness check: compare the stored hash against a freshly
      # computed hash of *current_source*.
      def stale?(current_source : String) : Bool
        Digest::SHA256.hexdigest(current_source)[0, 8].to_u32(16) != @source_hash
      end
    end
  end
end
