#!/usr/bin/env ruby
$gem, *$gemfiles = ARGV

def gets(msg, defaults = nil)
  if defaults
    msg = "#{msg} (default: #{defaults.inspect}) > "
  else
    msg = "#{msg} > "
  end
  print(msg)
  answer = $stdin.gets.strip

  if defaults && answer.empty?
    defaults
  else
    answer
  end
end

def die(msg)
  puts msg
  exit 1
end

if $gem.nil? || $gemfiles.empty?
  die("Usage: #{$0} GEM GEMFILE1 [GEMFILE2 ...]")
end

gemfiles_changed = false

$gemfiles.each do |gemfile|
  tag = newtag = match = matches = comment = nil

  pattern = /gem\s+['"]#{$gem}['"]/
  tag_pattern = /(tag:|['"]tag['"])\s+(=>)?['"](?<tag>.+)['"]/
  version_pattern = /gem[^'"]+['"]#{$gem}['"][^'"]+['"](?<tag>.+)['"]/

  puts "Looking to #{gemfile.inspect} for #{$gem.inspect}"
  File.exists?(gemfile) || puts("#{gemfile.inspect} not exists") || break

  file = IO.read(gemfile).lines
  matches = file.grep(pattern)
  matches.any? || puts("#{gemfile.inspect} doesn't contain #{$gem.inspect}") || break
  matches.size == 1 || puts("#{gemfile.inspect} contains #{matches.size} of #{$gem.inspect}") || break

  match = matches.first

  tag = match[tag_pattern, 'tag'] || match[version_pattern, 'tag']
  comment = (index = file.find_index(match)) &&
    (comment = file[index-1]) && comment =~ /tag:\s/ && comment
  puts "Current tag/version is #{tag.inspect}"
  if comment
    puts "Found comment: #{comment.inspect}"
  end

  newtag =
    if tag.split('.').last =~ /[0-9]/
      newtag = tag.split('.').tap do |ary|
        num = ary.pop
        num = num.to_i + 1
        ary.push(num.to_s)
      end.join('.')
      gets("Please enter new tag to replace #{tag.inspect}", newtag)
    else
      gets("Please enter new tag to replace #{tag.inspect}")
    end

  newtag.empty? && (puts "New tag can't be empty" || break)

  file = file.map do |line|
    if line == match
      match.sub(tag, newtag)
    else
      line
    end
  end
  IO.write(gemfile, file.join(""))

  puts "Replaced #{tag.inspect} with #{newtag.inspect} in #{gemfile.inspect}"

  if comment
    puts "Replacing comment..."

    tag = comment.split('tag:')[1].strip
    newtag =
      if tag.split('.').last =~ /[0-9]/
        newtag = tag.split('.').tap do |ary|
          num = ary.pop
          num = num.to_i + 1
          ary.push(num.to_s)
        end.join('.')
        gets("Enter new comment tag", newtag)
      else
        gets("Enter new comment tag")
      end

    file = file.map do |line|
      if line == comment
        comment.sub(tag, newtag)
      else
        line
      end
    end
    IO.write(gemfile, file.join(''))
  end

  gemfiles_changed = true

  puts
end

exec('bundle install') if gemfiles_changed
