# typed: true
# frozen_string_literal: true

module Homebrew
  module Bootsnap
    def self.key
      @key ||= begin
        require "digest/sha2"

        checksum = Digest::SHA256.new
        checksum << RUBY_VERSION
        checksum << RUBY_PLATFORM
        checksum << Dir.children(File.join(Gem.paths.path, "gems")).join(",")

        checksum.hexdigest
      end
    end

    private_class_method def self.cache_dir
      cache = ENV.fetch("HOMEBREW_CACHE", nil) || ENV.fetch("HOMEBREW_DEFAULT_CACHE", nil)
      raise "Needs `$HOMEBREW_CACHE` or `$HOMEBREW_DEFAULT_CACHE`!" if cache.nil? || cache.empty?

      File.join(cache, "bootsnap", key)
    end

    private_class_method def self.ignore_directories
      # We never do `require "vendor/bundle/ruby/..."` or `require "vendor/portable-ruby/..."`,
      # so let's slim the cache a bit by excluding them.
      # Note that gems within `bundle/ruby` will still be cached - these are when directory walking down from above.
      [
        (HOMEBREW_LIBRARY_PATH/"vendor/bundle/ruby").to_s,
        (HOMEBREW_LIBRARY_PATH/"vendor/portable-ruby").to_s,
      ]
    end

    private_class_method def self.enabled?
      !ENV["HOMEBREW_BOOTSNAP_GEM_PATH"].to_s.empty? && ENV["HOMEBREW_NO_BOOTSNAP"].nil?
    end

    def self.load!(compile_cache: true)
      return unless enabled?

      begin
        require ENV.fetch("HOMEBREW_BOOTSNAP_GEM_PATH")
      rescue LoadError
        return
      end

      ::Bootsnap.setup(
        cache_dir:,
        ignore_directories:,
        # In development environments the bootsnap compilation cache is
        # generated on the fly when source files are loaded.
        # https://github.com/Shopify/bootsnap?tab=readme-ov-file#precompilation
        development_mode:   true,
        load_path_cache:    true,
        # Ruby refuses InstructionSequence#to_binary while Coverage is active.
        compile_cache_iseq: compile_cache && ENV["HOMEBREW_TESTS_COVERAGE"].nil?,
        compile_cache_yaml: compile_cache,
      )
    end

    def self.reset!
      return unless enabled?

      ::Bootsnap.unload_cache!
      @key = nil

      # The compile cache doesn't get unloaded so we don't need to load it again!
      load!(compile_cache: false)
    end

    # Compile caches for the load graphs of common commands in a detached
    # background process, so the next `brew` command doesn't pay the cost of
    # compiling caches for Ruby files changed by e.g. `brew update`.
    def self.prewarm!
      return unless enabled?
      return if ENV["HOMEBREW_TESTS"]

      pid = Process.spawn(
        *HOMEBREW_RUBY_EXEC_ARGS,
        "-I", $LOAD_PATH.join(File::PATH_SEPARATOR),
        "-rglobal", "-rcmd/install", "-rcmd/fetch", "-rcmd/upgrade",
        "-e", "",
        in: File::NULL, out: File::NULL, err: File::NULL, pgroup: true
      )
      Process.detach(pid)
    rescue SystemCallError
      nil
    end
  end
end

Homebrew::Bootsnap.load!
