require 'language_pack/base'
require 'language_pack/shell_helpers'

module LanguagePack::Installers; end

class LanguagePack::Installers::HerokuRubyInstaller
  BASE_URL = LanguagePack::Base::VENDOR_URL
  BIN_DIR = Pathname("bin")

  include LanguagePack::ShellHelpers
  attr_reader :fetcher

  def initialize(stack: , multi_arch_stacks: , arch: , report: HerokuBuildReport::GLOBAL)
    @report = report
    if multi_arch_stacks.include?(stack)
      @fetcher = LanguagePack::Fetcher.new(BASE_URL, stack: stack, arch: arch)
    else
      @fetcher = LanguagePack::Fetcher.new(BASE_URL, stack: stack)
    end
  end

  def install(ruby_version, install_dir)
    @report.capture(
      # i.e. `jruby` or `ruby`
      "ruby_version_engine" => ruby_version.engine,
      # i.e. `ruby-3.4.2-jruby-10.0.2.0` or `ruby-3.4.2` or `ruby-3.5.0.pre1`
      "ruby_version_unique" => ruby_version.version_for_download,
      # i.e. `default` or `Gemfile.lock`
      "ruby_version_origin" => ruby_version.default? ? "default" : "Gemfile.lock",
    )

    case ruby_version.engine
    when :ruby
      @report.capture(
        "ruby_version_major_minor" => "#{ruby_version.major}.#{ruby_version.minor}",
        "ruby_version_full" => ruby_version.engine_version_full
      )
    when :jruby
      @report.capture(
        # i.e. `3.4.2` the target spec ruby version
        "jruby_version_ruby_version" => ruby_version.ruby_version,
        # i.e. `10.0.2.0` the version of jruby
        "jruby_version_full" => ruby_version.engine_version_full,
        # i.e. `9.4` or `10.0`
        "jruby_version_major_minor" => [
          ruby_version.engine_version.split(".")[0],
          ruby_version.engine_version.split(".")[1]
        ].join(".")
      )
    else
      raise "Internal error: Unknown engine: #{ruby_version.engine}"
    end

    fetch_unpack(ruby_version, install_dir)
    setup_binstubs(install_dir)
  end

  def fetch_unpack(ruby_version, install_dir)
    FileUtils.mkdir_p(install_dir)
    Dir.chdir(install_dir) do
      @fetcher.fetch_untar("#{ruby_version.version_for_download}.tgz")
    end
  end

  private def setup_binstubs(install_dir)
    BIN_DIR.mkpath
    run("ln -s ruby #{install_dir}/bin/ruby.exe")

    install_pathname = Pathname.new(install_dir)
    Dir["#{install_dir}/bin/*"].each do |vendor_bin|
      # for Ruby 2.6.0+ don't symlink the Bundler bin so our shim works
      next if vendor_bin.include?("bundle")

      # The bin/rake binstub generated when compiling ruby does not load bundler
      # which can cause unexpected failures. Deleting this binstub allows two things:
      #
      #   - If the app includes a custom binstub allows it to be used
      #   - If the app does not include a custom binstub, then it will fall back to vendor/bundle/bin/rake
      #     which is generated by bundler
      #
      # Discussion: https://github.com/heroku/heroku-buildpack-ruby/issues/1025#issuecomment-653102430
      next if vendor_bin.include?("rake")

      if install_pathname.absolute?
        run("ln -s #{vendor_bin} #{BIN_DIR}")
      else
        run("ln -s ../#{vendor_bin} #{BIN_DIR}")
      end
    end
  end
end
