require "../completion_provider"

module Lattice
  module Completion
    module Providers
      # RequireProvider completes `require "..."` paths.
      # It resolves relative paths from the current file's directory and
      # offers shard names from the workspace `lib/` directory.
      class RequireProvider < CompletionProvider
        def completions(
          uri : String,
          source : String,
          position : ::LSP::Position,
        ) : Array(::LSP::CompletionItem)
          items = [] of ::LSP::CompletionItem

          lines = source.split('\n')
          return items if position.line >= lines.size

          line = lines[position.line]
          # Only activate inside require "..." on the current line.
          return items unless line.match(/^\s*require\s+"([^"]*)"?/)

          partial = $~[1]? || ""
          file_path = uri_to_path(uri)
          base_dir = File.dirname(file_path)

          # Relative completions from the same directory.
          if partial.starts_with?("./") || partial.starts_with?("../") || partial.empty? || partial.starts_with?(".")
            dir = partial.empty? ? base_dir : File.expand_path(File.join(base_dir, File.dirname(partial)), base_dir)
            prefix = File.basename(partial)
            if Dir.exists?(dir)
              Dir.each_child(dir) do |entry|
                full = File.join(dir, entry)
                if Dir.exists?(full)
                  item = ::LSP::CompletionItem.new("#{partial.rchop(File.basename(partial))}#{entry}/")
                  item.kind = ::LSP::CompletionItemKind::Folder
                  items << item
                elsif entry.ends_with?(".cr") && entry.starts_with?(prefix)
                  label = entry.rchop(".cr")
                  item = ::LSP::CompletionItem.new("#{partial.rchop(File.basename(partial))}#{label}")
                  item.kind = ::LSP::CompletionItemKind::File
                  items << item
                end
              end
            end
          else
            # Shard / stdlib name completions from lib/ directory.
            workspace_root = find_workspace_root(base_dir)
            if workspace_root
              lib_dir = File.join(workspace_root, "lib")
              if Dir.exists?(lib_dir)
                Dir.each_child(lib_dir) do |shard|
                  shard_path = File.join(lib_dir, shard, "src")
                  next unless Dir.exists?(shard_path)
                  item = ::LSP::CompletionItem.new(shard)
                  item.kind = ::LSP::CompletionItemKind::Module
                  items << item
                end
              end
            end
          end

          items
        end

        private def uri_to_path(uri : String) : String
          return uri unless uri.starts_with?("file://")
          p = uri.sub(/^file:\/\//, "")
          p = URI.decode(p)
          p = p[1..] if p =~ /^\/([a-zA-Z]):/
          p.gsub('/', File::SEPARATOR)
        end

        # Walk up from *dir* looking for a shard.yml (workspace root marker).
        private def find_workspace_root(dir : String) : String?
          cur = dir
          10.times do
            return cur if File.exists?(File.join(cur, "shard.yml"))
            parent = File.dirname(cur)
            break if parent == cur
            cur = parent
          end
          nil
        end
      end
    end
  end
end
