require "json"
require "../crystal/index/index"
require "../crystal/workspace_analyzer"

module Lattice
  module CLI
    # Dumps all indexed symbols as JSON or plain text. Useful for scripting and
    # IDE integrations that need a symbol list without starting the full LSP
    # server.
    #
    # Usage:
    #   lattice list-symbols [--kind=<kind>] [--format=json|text] [path]
    #
    # Options:
    #   --kind=<kind>      Filter by symbol kind (class, module, method, …)
    #   --format=json      Output as a JSON array (default)
    #   --format=text      Output one "kind  name  file:line" entry per line
    #   path               Workspace root to scan (default: current directory)
    class ListSymbols
      def self.run(args : Array(String)) : NoReturn
        kind_filter = nil
        format = "json"
        root = Dir.current

        args.each do |arg|
          if arg.starts_with?("--kind=")
            kind_filter = arg.sub("--kind=", "")
          elsif arg.starts_with?("--format=")
            format = arg.sub("--format=", "")
          elsif !arg.starts_with?("-")
            root = arg
          end
        end

        unless %w[json text].includes?(format)
          STDERR.puts "lattice list-symbols: unknown format '#{format}' (use json or text)"
          exit 1
        end

        unless Dir.exists?(root)
          STDERR.puts "lattice list-symbols: directory not found: #{root}"
          exit 1
        end

        index = Index::WorkspaceIndex.new(root)

        Dir.glob(File.join(root, "**", "*.cr")).sort.each do |file|
          next if file.includes?(".lattice-cache")
          source = File.read(file)
          index.index_file(file, source)
        rescue ex
          STDERR.puts "lattice list-symbols: warning: could not read #{file}: #{ex.message}"
        end

        symbols = if kf = kind_filter
                    index.all_of_kind(kf)
                  else
                    # Gather all symbols across every kind.
                    all = [] of Index::SymbolEntry
                    %w[class module struct enum lib method property instance_variable constant macro].each do |k|
                      all.concat(index.all_of_kind(k))
                    end
                    all
                  end

        case format
        when "json"
          rows = symbols.map do |s|
            {
              "name"   => JSON::Any.new(s.name),
              "kind"   => JSON::Any.new(s.kind),
              "file"   => JSON::Any.new(s.file),
              "line"   => JSON::Any.new(s.line.to_i64),
              "column" => JSON::Any.new(s.column.to_i64),
            }
          end
          puts JSON::Any.new(rows.map { |r| JSON::Any.new(r) }).to_json
        when "text"
          symbols.each do |s|
            puts "%-20s %-25s %s:%d" % [s.kind, s.name, s.file, s.line + 1]
          end
        end

        exit 0
      end
    end
  end
end
