namespace :heroku_ssl do

task :update_certs do
  update_certs
end

task :generate_certs do
  email = (ARGV[1] || '').strip
  email = get_email if email.blank?

  puts "Registering #{email}"
  HerokuSsl::register email

  domain = ARGV[2..-1]
  domain = get_domains if domain.blank?

  puts "Authorizing and generating certificates for #{domain}"

  certs = HerokuSsl::request_certificate domain

  if certs.present?
    STDOUT.puts '~~ GENERATED CERTIFICATES START ~~'
    STDOUT.puts JSON(certs)
    STDOUT.puts '~~ GENERATED CERTIFICATES END ~~'
  end
end

def update_certs
  STDOUT.puts 'Once your app has been deployed to Heroku, hit enter.'

  STDIN.gets

  email = get_email
  domains = get_domains
  app = get_app

  puts "Attempting to generate ssl certificates for #{app} (registering #{domains} to #{email})"

  #generate the certs on the server
  output = heroku_run("run rake heroku_ssl:generate_certs #{email} #{domains} --app #{app}")

  #read out the certs to temporary files
  unless output.include? '~~ GENERATED CERTIFICATES START ~~'
    puts 'Full log: '
    puts output
    puts ''

    puts 'Could not generate certificates. Please try again later or try running `heroku run rake heroku_ssl:generate_certs` directly'
    return
  end

  output = output.split('~~ GENERATED CERTIFICATES START ~~').last
               .split('~~ GENERATED CERTIFICATES END ~~').first
  output = JSON(output).with_indifferent_access

  unless output['fullchain'].present? && output['privkey'].present?
    puts 'Output: '
    puts output
    puts ''

    puts 'Failed to read certificates'
    return
  end

  puts 'Successfully generated certificates! Attempting to update Heroku DNS'

  File.open('fullchain.pem', 'wb') do |file|
    file.write output['fullchain']
  end

  File.open('privkey.pem', 'wb') do |file|
    file.write output['privkey']
  end

  # update heroku certs

  if heroku_run('certs') =~ /has\sno\sSSL\scertificates/i
    heroku_run("certs:add fullchain.pem privkey.pem --app #{get_app}")
  else
    heroku_run("certs:update fullchain.pem privkey.pem --app #{get_app} --confirm #{get_app}")
  end

  # clean up
  File.delete('fullchain.pem', 'privkey.pem')

  puts 'Successfully updated Heroku SSL certificates! Now you just need to make sure your DNS is configured to point as follows: '
  puts heroku_run('domains').split("\n")[4..-1].join("\n")
end

def get_email
  return @email if @email.present?

  @email = `git config user.email`
  @email.strip! if @email.present?

  default_prompt = ''
  default_prompt = " [#{@email}]" if @email.present?

  new_email = nil
  while new_email.blank?
    STDOUT.puts "Enter your email (used to notify you of expiration)#{default_prompt}: "
    new_email = STDIN.gets

    if new_email.blank?
      new_email = @email
    else
      new_email.strip!
    end
  end

  @email = new_email
end

def heroku_run(command)
  # RUBYOPT breaks the heroku command for some reason, so you have to unset it
  result = `unset RUBYOPT; heroku #{command}`

  if result =~ /rake\saborted/i
    puts "Don't know how to build task -- make sure you have deployed a version with this gem installed to heroku"
  end

  if result =~ /No\ssuch\sfile\sor\sdirectory/i || result =~ /command\snot\sfound/i
    puts 'Cannot run command heroku -- are you sure you have it installed?'
  end

  if result =~ /Bundler::GemNotFound/i
    puts 'Please log in to heroku by running `heroku login`'
  end

  result
end

def get_domains
  return @domain if @domain.present?

  domains = heroku_run('domains').split("\n").select(&:present?)[5..-1]
  if domains.blank?
    puts 'Warning: Could not load domains'
    domains = []
  end
  domains.map! do |domain|
    domain.split(/\s+/).first
  end
  @domain = domains.join ' '

  default_prompt = ''
  default_prompt = " [#{@domain}]" if @domain.present?

  new_domain = nil
  while new_domain.blank?
    STDOUT.puts "Enter the domain to register#{default_prompt}: "
    new_domain = STDIN.gets

    if new_domain.blank?
      new_domain = @domain
    else
      new_domain.strip!
    end
  end

  @domain = new_domain
end

def get_app
  return @app if @app.present?

  @apps = @apps || heroku_run('apps').split("\n").map(&:split).map(&:first).select(&:present?)
  remotes = `git remote -v`.split("\n").map do |r|
    r.split("\t").last
  end

  @apps.each do |app|
    remotes.each do |remote|
      if remote.include? app
        @app = app
        break
      end
    end
    break if @app.present?
  end

  default_prompt = ''
  default_prompt = " [#{@app}]" if @app.present?

  new_app = nil
  while new_app.blank?
    STDOUT.puts "Enter the heroku app#{default_prompt}: "
    new_app = STDIN.gets

    if new_app.blank?
      new_app = @app
    else
      new_app.strip!
    end
  end

  @app = new_app
end

end