require 'heroku_san'
require 'heroku_san/git'
include HerokuSan::Git

if defined?(Rails)
  HerokuSan.project ||= HerokuSan::Project.new(
                          Rails.root.join("config", "heroku.yml"),
                          :deploy => HerokuSan::Deploy::Rails
                          )
end

HerokuSan.project.all.each do |stage|
  desc "Select #{stage} Heroku app for later commands"
  task "heroku:stage:#{stage}" do
    HerokuSan.project << stage
  end
  task stage => "heroku:stage:#{stage}"
end

namespace :heroku do
  desc 'Select all Heroku apps for later command'
  task 'stage:all' do
    HerokuSan.project << HerokuSan.project.all
  end

  desc "Creates the Heroku app"
  task :create do
    each_heroku_app do |stage|
      puts "#{stage.name}: Created #{stage.create}"
    end
  end

  #desc "Generate the Heroku gems manifest from gem dependencies"
  task :gems => 'gems:base' do
    raise HerokuSan::Deprecated
  end

  desc 'Add git remotes for all apps in this project'
  task :remotes do
    each_heroku_app do |stage|
      sh "git remote add #{stage.name} #{stage.repo}"
    end
  end

  task :share do
    raise HerokuSan::Deprecated
  end

  task :unshare do
    raise HerokuSan::Deprecated
  end

  desc 'Lists configured apps'
  task :apps => :all do
    each_heroku_app do |stage|
      rev = stage.revision
      puts  "#{stage.name} is shorthand for the Heroku app #{stage.app} located at:"
      puts  "  #{stage.repo}"
      puts  "  @ #{(rev == '') ? 'not deployed' : rev}"
      puts
    end
  end

  namespace :apps do
    desc 'Lists configured apps without hitting heroku'
    task :local => :all do
      each_heroku_app do |stage|
        puts "#{stage.name} is shorthand for the Heroku app #{stage.app} located at:"
        puts "  #{stage.repo}"
        puts "  the #{stage.name} TAG is '#{stage.tag}'" if stage.tag
        puts
      end
    end
  end

  desc 'Add config:vars to each application.'
  task :config do
    each_heroku_app do |stage|
      stage.push_config.each do |(key,value)|
        puts "#{key}: #{value}"
      end
    end
  end

  desc 'Install addons for the application.'
  task :addons do
    each_heroku_app do |stage|
      addons = stage.install_addons
      puts "#{stage.name} addons"
      addons.each do |addon| 
        puts "  - " + addon['name'] + (addon['configured'] ? "" : " # Configure at https://api.heroku.com/myapps/#{stage.app}/addons/#{addon['name']}")
      end
    end
  end

  namespace :addons do
    desc 'List configured addons, without installing them'
    task :local do
      each_heroku_app do |stage|
        puts "Configured addons for #{stage.name}:"
        stage.addons.each do |addon|
          puts "  - #{addon}"
        end
      end
    end
  end

  desc 'Creates an example configuration file'
  task :create_config do
    filename = %Q{#{HerokuSan.project.config_file.to_s}}
    if HerokuSan.project.create_config
      puts "Copied example config to #{filename.inspect}"
      if ENV['EDITOR'] && ENV['EDITOR'] != ''
        sh "#{ENV['EDITOR']} #{filename}"
      else
        puts "Please edit #{filename.inspect} with your application's settings."
      end
    else
      puts "#{filename.inspect} already exists"
    end
  end

  namespace :config do
    desc 'Add proper RACK_ENV to each application'
    task :rack_env => :all do
      each_heroku_app do |stage|
        command = "heroku config --app #{stage.app}"
        puts command
        config = Hash[`#{command}`.scan(/^(.+?)\s*=>\s*(.+)$/)]
        if config['RACK_ENV'] != stage.name
          puts stage.push_config 'RACK_ENV' => stage.name
        end
      end
    end

    desc "Lists config variables as set on Heroku"
    task :list do
      each_heroku_app do |stage|
        puts "#{stage.name}:"
        stage.long_config.each do |(key,value)|
          puts "#{key}: #{value}"
        end
      end
    end

    namespace :list do
      desc "Lists local config variables without setting them"
      task :local do
        each_heroku_app do |stage|
          puts "#{stage.name}:"
          stage.config.each do |(key,value)|
            puts "#{key}: #{value}"
          end
        end
      end
    end
  end

  desc 'Runs a rake task remotely'
  task :rake, [:task] do |t, args|
    each_heroku_app do |stage|
      puts stage.run "rake #{args.task}"
    end
  end

  desc "Pushes the given commit (default: HEAD)"
  task :push, :commit do |t, args|
    each_heroku_app do |stage|
      stage.push(args.commit)
    end
  end

  namespace :push do
    desc "Force-pushes the given commit (default: HEAD)"
    task :force, :commit do |t, args|
      each_heroku_app do |stage|
        stage.push(args.commit, :force)
      end
    end
  end

  desc "Enable maintenance mode"
  task :maintenance do
    each_heroku_app do |stage|
      stage.maintenance :on
      puts "#{stage.name}: Maintenance mode enabled."
    end
  end

  desc "Enable maintenance mode"
  task :maintenance_on do
    each_heroku_app do |stage|
      stage.maintenance :on
      puts "#{stage.name}: Maintenance mode enabled."
    end
  end

  desc "Disable maintenance mode"
  task :maintenance_off do
    each_heroku_app do |stage|
      stage.maintenance :off
      puts "#{stage.name}: Maintenance mode disabled."
    end
  end

  desc "Deploys the app (default: HEAD)"
  task :deploy, [:commit] => [:before_deploy] do |t, args|
    each_heroku_app do |stage|
      stage.deploy(args.commit)
    end
    Rake::Task[:after_deploy].execute
  end

  namespace :deploy do
    desc "Deploys the app with push --force (default: HEAD)"
    task :force, [:commit] => [:before_deploy] do |t, args|
      each_heroku_app do |stage|
        stage.deploy(args.commit, :force)
      end
      Rake::Task[:after_deploy].execute
    end

    desc "Callback before deploys"
    task :before do
    end

    desc "Callback after deploys"
    task :after do
  end

  end

  task :force_deploy do
    raise HerokuSan::Deprecated
  end

  #desc "Captures a bundle on Heroku"
  task :capture do
    raise HerokuSan::Deprecated
  end

  desc "Opens a remote console"
  task :console do
    each_heroku_app do |stage|
      stage.run 'console'
    end
  end

  desc "Restarts remote servers"
  task :restart do
    each_heroku_app do |stage|
      stage.restart
      puts "#{stage.name}: Restarted."
    end
  end

  namespace :logs do
    task :default do
      each_heroku_app do |stage|
        stage.logs
      end
    end

    desc "Tail the Heroku logs (requires logging:expanded)"
    task :tail do
      each_heroku_app do |stage|
        stage.logs(:tail)
      end
    end
  end

  desc "Shows the Heroku logs"
  task :logs => 'logs:default'

  namespace :db do
    desc "Migrates and restarts remote servers"
    task :migrate do
      each_heroku_app do |stage|
        stage.migrate
      end
    end

    desc "Pull database from stage to local dev database"
    task :pull do
      each_heroku_app do |stage|
        sh "heroku db:pull --app #{stage.app} --confirm #{stage.app}"
      end
    end
  end

  desc "Run a bash shell on Heroku"
  task :shell do
    each_heroku_app do |stage|
      stage.run 'bash'
    end
  end
end


def alias_task(hash)
  hash.each_pair do |(new_task, original_task)|
    the_task = Rake.application[original_task]
    task new_task, {the_task.arg_names => [original_task]}
  end
end

alias_task :all => 'heroku:stage:all'
alias_task :deploy => 'heroku:deploy'
alias_task 'deploy:force' => 'heroku:deploy:force'
alias_task :before_deploy => 'heroku:deploy:before'
alias_task :after_deploy => 'heroku:deploy:after'
alias_task :console => 'heroku:console'
alias_task :restart => 'heroku:restart'
alias_task :migrate => 'heroku:db:migrate'
alias_task :logs => 'heroku:logs:default'
alias_task 'logs:tail' => 'heroku:logs:tail'
alias_task 'heroku:rack_env' => 'heroku:config:rack_env'
alias_task :shell => 'heroku:shell'

def each_heroku_app(&block)
  HerokuSan.project.each_app(&block)
  puts
rescue HerokuSan::NoApps => e
  puts "You must first specify at least one Heroku app:
    rake <app> [<app>] <command>
    rake production restart
    rake demo staging deploy"

  puts "\nYou can use also command all Heroku apps for this project:
    rake all restart"

  exit(1)
end
