#!/usr/bin/env ruby
# frozen_string_literal: true

require 'fileutils'
require 'json'
require 'netrc'
require 'octokit'
require 'shellwords'
require 'uri'

def run(*args)
  puts('<exec> ' + args.map { |s| Shellwords.escape(s) }.join(' '))
  system(*args)
end

if ARGV.size != 1
  $stderr.puts "usage: #{$PROGRAM_NAME} [gem-name]"
  exit 1
end

gem_name = ARGV[0]

version_constant =
  {
    'nanoc' => 'Nanoc::VERSION',
    'nanoc-core' => 'Nanoc::Core::VERSION',
    'nanoc-cli' => 'Nanoc::CLI::VERSION',
    'nanoc-checking' => 'Nanoc::Checking::VERSION',
    'nanoc-dart-sass' => 'Nanoc::DartSass::VERSION',
    'nanoc-deploying' => 'Nanoc::Deploying::VERSION',
    'nanoc-external' => 'Nanoc::External::VERSION',
    'nanoc-org-mode' => 'Nanoc::OrgMode::VERSION',
    'nanoc-live' => 'Nanoc::Live::VERSION',
    'nanoc-spec' => 'Nanoc::Spec::VERSION',
    'nanoc-tilt' => 'Nanoc::Tilt::VERSION',
    'guard-nanoc' => 'Guard::GUARD_NANOC_VERSION',
  }.fetch(gem_name) do
    $stderr.puts "Error: Unknown gem name: #{gem_name}"
    exit 1
  end

gem_dir =
  {
    'nanoc' => 'nanoc',
    'nanoc-core' => 'nanoc-core',
    'nanoc-cli' => 'nanoc-cli',
    'nanoc-checking' => 'nanoc-checking',
    'nanoc-dart-sass' => 'nanoc-dart-sass',
    'nanoc-deploying' => 'nanoc-deploying',
    'nanoc-external' => 'nanoc-external',
    'nanoc-org-mode' => 'nanoc-org-mode',
    'nanoc-live' => 'nanoc-live',
    'nanoc-spec' => 'nanoc-spec',
    'nanoc-tilt' => 'nanoc-tilt',
    'guard-nanoc' => 'guard-nanoc',
  }.fetch(gem_name) do
    $stderr.puts "Error: Unknown gem name: #{gem_name}"
    exit 1
  end

gem_path =
  {
    'nanoc' => 'nanoc',
    'nanoc-core' => 'nanoc/core',
    'nanoc-cli' => 'nanoc/cli',
    'nanoc-checking' => 'nanoc/checking',
    'nanoc-dart-sass' => 'nanoc/dart_sass',
    'nanoc-deploying' => 'nanoc/deploying',
    'nanoc-external' => 'nanoc/external',
    'nanoc-org-mode' => 'nanoc/org_mode',
    'nanoc-live' => 'nanoc/live',
    'nanoc-spec' => 'nanoc/spec',
    'nanoc-tilt' => 'nanoc/tilt',
    'guard-nanoc' => 'guard/nanoc',
  }.fetch(gem_name) do
    $stderr.puts "Error: Unknown gem name: #{gem_name}"
    exit 1
  end

puts "version_constant = #{version_constant}"
puts "gem_dir = #{gem_dir}"
puts "gem_path = #{gem_path}"
puts

puts "=== Entering gem dir (#{gem_dir})…"
Dir.chdir(gem_dir)
puts Dir.getwd
puts

puts '=== Logging in to GitHub’s API…'
client = Octokit::Client.new(netrc: true)
puts

puts '=== Deleting old *.gem files…'
Dir['*.gem'].each do |fn|
  puts "deleting #{fn}…"
  FileUtils.rm_f(fn)
end
puts

unless %w[nanoc-core nanoc-cli].include?(gem_name)
  puts '=== Verifying presence of release date…'
  release_line = File.readlines('NEWS.md').drop(2).first
  unless / \(\d{4}-\d{2}-\d{2}\)$/.match?(release_line)
    $stderr.puts 'Error: No proper release date found!'
    exit 1
  end
  unless release_line.include?(Time.now.strftime('%Y-%m-%d'))
    $stderr.puts 'Error: The release date does not match today’s date!'
    exit 1
  end
  puts
end

puts '=== Reading version…'
require "./lib/#{gem_path}/version"
version = eval(version_constant) # rubocop:disable Security/Eval
puts "Version = #{version}"
puts

puts '=== Building gems…'
run('bundle', 'exec', 'rake', 'gem')
puts

puts '=== Verifying that gems were built properly…'
gem_filename = "#{gem_name}-#{version}.gem"
unless File.file?(gem_filename)
  $stderr.puts "Error: Could not find gem: #{gem_filename}"
  exit 1
end
puts

puts '=== Verifying that gem version does not yet exist…'
url = URI.parse("https://rubygems.org/api/v1/versions/#{gem_name}.json")
response = Net::HTTP.get_response(url)
existing_versions =
  case response.code
  when '404'
    []
  when '200'
    JSON.parse(response.body).map { |e| e.fetch('number') }
  else
    $stderr.puts "Error: Couldn’t fetch version information for #{gem_name} (status #{response.code})"
    exit 1
  end
if existing_versions.include?(version)
  $stderr.puts "Error: #{gem_name} v#{version} already exists"
  exit 1
end
puts

puts '=== Verifying that release does not yet exist…'
releases = client.releases('nanoc/nanoc')
release = releases.find { |r| r.tag_name == version }
if release
  $stderr.puts 'Error: GitHub release already exists!'
  exit 1
end
puts

puts '=== Reading release notes…'
release_notes =
  File.readlines('NEWS.md')
      .drop(4)
      .take_while { |l| l !~ /^## / }
      .join
puts

puts '=== Creating Git tag…'
annotation =
  if gem_name == 'nanoc'
    version
  else
    "#{gem_name}-v#{version}"
  end
run('git', 'tag', '--sign', '--annotate', annotation, '--message', "#{gem_name} v#{version}")
puts

puts '=== Pushing Git data…'
run('git', 'push', 'origin', '--tags')
puts

puts '=== Pushing gem…'
run('gem', 'push', gem_filename)
puts

if gem_name == 'nanoc'
  puts '=== Creating release on GitHub…'
  sleep 3 # Give GitHub some time to detect the new tag
  is_prerelease = version =~ /a|b|rc/ || version =~ /^0/
  client.create_release(
    'nanoc/nanoc', version,
    prerelease: !is_prerelease.nil?,
    body: release_notes
  )
  puts
end

puts 'DONE!'
