#!/usr/local/bin/ruby
# update_ruby_gem_packages version 2.3 (for Chromebrew)
# Author: Satadru Pramanik (satmandu) satadru at gmail dot com
# Usage in root of cloned chromebrew repo:
# tools/update_ruby_gem_packages.rb

# Add >LOCAL< lib to LOAD_PATH
$LOAD_PATH.unshift './lib'

require 'json'
require 'net/http'
require_relative '../lib/color'
require_relative '../lib/const'
require_relative '../lib/convenience_functions'
require_relative '../lib/package'
require_relative '../lib/package_utils'
require_relative '../lib/require_gem'
require_gem 'concurrent-ruby'
require_gem 'ruby-libversion', 'ruby_libversion'

# Make sure rubygems is up to date.
system 'gem update --system'

def check_for_updated_ruby_packages
  # Create a thread pool for parallelization.
  pool = Concurrent::ThreadPoolExecutor.new(
    min_threads: 1,
    max_threads: CREW_NPROC.to_i + 1,
    max_queue: 0, # unbounded work queue
    fallback_policy: :caller_runs
  )

  # Currently, the only packages that use the ruby buildsystem are ruby_* packages, so this finds all the packages we need to check.
  relevant_gem_packages = Dir['packages/ruby_*.rb']

  # Get the total number of files to check, and then the length of that number, so status updates can be formatted.
  total_files_to_check = relevant_gem_packages.length
  numlength = total_files_to_check.to_s.length
  updateable_packages = {}
  packages_without_gem_versions = []
  relevant_gem_packages.each_with_index do |package, index|
    pool.post do
      pkg = Package.load_package(package)
      gem_name = pkg.name.sub('ruby_', '')
      # We replace all dashes with underscores in our initial package names, but some gems actually use underscores, so we need special cases.
      # This list was created by looking at what packages were listed as not having updates in rubygems, and then looking up the upstream name for them.
      if %w[
        connection_pool error_highlight jaro_winkler
        lint_roller method_source mini_mime multi_xml mutex_m
        power_assert regexp_parser repl_type_completor ruby2_keywords
        syntax_suggest
      ].include?(gem_name)
        # These gems used underscores originally, so don't replace anything
      elsif gem_name == 'language_server_protocol'
        # These gems have an underscore then a dash, but there's only one, so we hardcode the logic for now.
        gem_name = 'language_server-protocol'
      elsif gem_name == 'unicode_display_width'
        # These gems have a dash then an underscore, but there's only one, so we hardcode the logic for now.
        gem_name = 'unicode-display_width'
      else
        # In the common case, the gem name used only dashes, which we all replaced with underscores.
        gem_name.gsub!('_', '-')
      end

      puts "[#{(index + 1).to_s.rjust(numlength)}/#{total_files_to_check}] Checking rubygems for updates to #{gem_name} in #{package}...".orange

      gem_version = JSON.parse(Net::HTTP.get(URI("https://rubygems.org/api/v1/versions/#{gem_name}/latest.json")))['version']

      if gem_version == 'unknown'
        packages_without_gem_versions << gem_name
        next
      end

      # Any version with a letter is considered a prerelease as per
      # https://docs.ruby-lang.org/en/master/Gem/Version.html#method-i-prerelease-3F
      next if gem_version.match?(/[a-zA-Z]/)

      next unless Libversion.version_compare2(PackageUtils.get_clean_version(pkg.version), gem_version) == -1

      updateable_packages[package] = gem_version
    end
  end
  pool.shutdown
  pool.wait_for_termination

  puts "Done checking rubygems for updates to #{total_files_to_check} ruby packages.\r".orange
  puts "Updated versions were not listed in rubygems for: #{packages_without_gem_versions.join(' ')}".orange unless packages_without_gem_versions.blank?

  return updateable_packages
end

def update_package_files(updateable_packages)
  return if updateable_packages.empty?

  updateable_packages.each_pair do |package, new_version|
    pkg = Package.load_package(package)
    puts "Updating #{pkg.name.gsub('_', '-')} from #{pkg.version} to #{new_version}".lightblue
    file = File.read(package)
    file.sub!(PackageUtils.get_clean_version(pkg.version), new_version)
    File.write(package, file)
  end
end

# If being run standalone, also update the package files with the new versions.
update_package_files(check_for_updated_ruby_packages)
