# frozen_string_literal: true

require 'rake'
require 'readline'
require 'fileutils'

# rubocop:disable Metrics/BlockLength
namespace :version do
  PROJECT_ROOT = File.expand_path(FileUtils.pwd).freeze
  PROJECT_NAME = ENV['PROJECT_NAME'] || File.basename(PROJECT_ROOT)

  desc 'Write changes to the CHANGELOG'
  task :changes do
    text = ask('CHANGELOG Entry:')
    text.insert(
      0,
      "*#{read_version.join('.')}* (#{Time.now.strftime('%B %d, %Y')})\n\n"
    )
    text << "\n"
    prepend_changelog(text)
    launch_editor(changelog)
  end

  desc 'Increment the patch version and write changes to the changelog'
  task :bump_patch do
    exit unless check_branch_and_warn
    major, minor, patch = read_version
    patch = patch.to_i + 1
    write_version_file([major, minor, patch])
    update_readme_version_strings
    Rake::Task['version:changes'].invoke
  end

  desc 'Alias for :bump_patch'
  task bump: :bump_patch

  desc 'Increment the minor version and write changes to the changelog'
  task :bump_minor do
    exit unless check_branch_and_warn
    major, minor, _patch = read_version
    minor = minor.to_i + 1
    patch = 0
    write_version_file([major, minor, patch])
    update_readme_version_strings
    Rake::Task['version:changes'].invoke
  end

  desc 'Increment the major version and write changes to the changelog'
  task :bump_major do
    exit unless check_branch_and_warn
    major, _minor, _patch = read_version
    major = major.to_i + 1
    minor = 0
    patch = 0
    write_version_file([major, minor, patch])
    update_readme_version_strings
    Rake::Task['version:changes'].invoke
  end

  private

  def version_file_path
    split = PROJECT_NAME.split('-')
    "#{PROJECT_ROOT}/lib/#{split.join('/')}/version.rb"
  end

  def module_name
    case PROJECT_NAME
    when /-/
      PROJECT_NAME.split('-').map(&:capitalize).join('::')
    when /_/
      PROJECT_NAME.split('_').map(&:capitalize).join
    else
      PROJECT_NAME.capitalize
    end
  end

  def read_version
    silence_warnings do
      load version_file_path
    end
    text = eval("#{module_name}::VERSION")
    text.split('.')
  end

  def write_version_file(version_array)
    version = version_array.join('.')
    new_version = %(  VERSION = '#{version}'.freeze)
    lines = File.readlines(version_file_path)
    File.open(version_file_path, 'w') do |f|
      lines.each do |line|
        if line =~ /VERSION/
          f.write("#{new_version}\n")
        else
          f.write(line)
        end
      end
    end
  end

  def update_readme_version_strings
    version_string = read_version.join('.')
    readme = open('README.md').read
    regex = /^\*\*Version: [0-9.]+\*\*$/i
    return nil unless readme =~ regex

    File.open('README.md', 'w') do |f|
      f.write(readme.gsub(regex, "**Version: #{version_string}**"))
    end
  end

  def changelog
    return @changelog_path if @changelog_path

    @changelog_path = File.join(PROJECT_ROOT, 'CHANGELOG')
    FileUtils.touch(@changelog_path)
    @changelog_path
  end

  def prepend_changelog(text_array)
    old = File.read(changelog).to_s.chomp
    text_array.push(old)
    File.open(changelog, 'w') do |f|
      text_array.flatten.each do |line|
        f.puts(line)
      end
    end
  end

  # rubocop:disable Lint/AssignmentInCondition
  def ask(message)
    response = []
    puts message
    puts 'Hit <Control>-D when finished:'
    while line = Readline.readline('* ', false)
      response << "* #{line.chomp}" unless line.nil?
    end
    response
  end
  # rubocop:enable Lint/AssignmentInCondition

  def current_branch
    `git symbolic-ref --short HEAD`.chomp
  end

  def branch_warning_message
    <<~STRING
      You typically do not want to bump versions on the 'master' branch
      unless you plan to rebase or back-merge into 'develop'.

      If you don't care or don't know what I'm talking about just enter 'y'
      and continue.

      Optionally, you can hit 'n' to abort and switch your branch to 'develop'
      or whatever branch you use for development, bump the version, merge to
      'master' then 'rake release'.

      Do you really want to bump the version on your 'master' branch? (y/n)
    STRING
  end

  def check_branch_and_warn
    return true unless current_branch == 'master'

    puts(branch_warning_message)
    while (line = $stdin.gets.chomp)
      return true if line =~ /[yY]/

      puts 'Aborting version bump.'
      return false
    end
  end

  def launch_editor(file)
    system("#{ENV['EDITOR']} #{file}") if ENV['EDITOR']
  end

  def silence_warnings
    original_verbosity = $VERBOSE
    $VERBOSE = nil
    yield
  ensure
    $VERBOSE = original_verbosity
  end
end
# rubocop:enable Metrics/BlockLength
