namespace :deploy do

task :starting do
  invoke 'deploy:check'
  invoke 'deploy:set_previous_revision'
end

task :updating => :new_release_path do
  invoke "#{scm}:create_release"
  invoke "deploy:set_current_revision"
  invoke 'deploy:symlink:shared'
end

task :reverting do
  invoke 'deploy:revert_release'
end

task :publishing do
  invoke 'deploy:symlink:release'
end

task :finishing do
  invoke 'deploy:cleanup'
end

task :finishing_rollback do
  invoke 'deploy:cleanup_rollback'
end

task :finished do
  invoke 'deploy:log_revision'
end

desc 'Check required files and directories exist'
task :check do
  invoke "#{scm}:check"
  invoke 'deploy:check:directories'
  invoke 'deploy:check:linked_dirs'
  invoke 'deploy:check:make_linked_dirs'
  invoke 'deploy:check:linked_files'
end

namespace :check do
  desc 'Check shared and release directories exist'
  task :directories do
    on release_roles :all do
      execute :mkdir, '-p', shared_path, releases_path
    end
  end

  desc 'Check directories to be linked exist in shared'
  task :linked_dirs do
    next unless any? :linked_dirs
    on release_roles :all do
      execute :mkdir, '-p', linked_dirs(shared_path)
    end
  end

  desc 'Check directories of files to be linked exist in shared'
  task :make_linked_dirs do
    next unless any? :linked_files
    on release_roles :all do |host|
      execute :mkdir, '-p', linked_file_dirs(shared_path)
    end
  end

  desc 'Check files to be linked exist in shared'
  task :linked_files do
    next unless any? :linked_files
    on release_roles :all do |host|
      linked_files(shared_path).each do |file|
        unless test "[ -f #{file} ]"
          error t(:linked_file_does_not_exist, file: file, host: host)
          exit 1
        end
      end
    end
  end
end

namespace :symlink do
  desc 'Symlink release to current'
  task :release do
    on release_roles :all do
      tmp_current_path = release_path.parent.join(current_path.basename)
      execute :ln, '-s', release_path, tmp_current_path
      execute :mv, tmp_current_path, current_path.parent
    end
  end

  desc 'Symlink files and directories from shared to release'
  task :shared do
    invoke 'deploy:symlink:linked_files'
    invoke 'deploy:symlink:linked_dirs'
  end

  desc 'Symlink linked directories'
  task :linked_dirs do
    next unless any? :linked_dirs
    on release_roles :all do
      execute :mkdir, '-p', linked_dir_parents(release_path)

      fetch(:linked_dirs).each do |dir|
        target = release_path.join(dir)
        source = shared_path.join(dir)
        unless test "[ -L #{target} ]"
          if test "[ -d #{target} ]"
            execute :rm, '-rf', target
          end
          execute :ln, '-s', source, target
        end
      end
    end
  end

  desc 'Symlink linked files'
  task :linked_files do
    next unless any? :linked_files
    on release_roles :all do
      execute :mkdir, '-p', linked_file_dirs(release_path)

      fetch(:linked_files).each do |file|
        target = release_path.join(file)
        source = shared_path.join(file)
        unless test "[ -L #{target} ]"
          if test "[ -f #{target} ]"
            execute :rm, target
          end
          execute :ln, '-s', source, target
        end
      end
    end
  end
end

desc 'Clean up old releases'
task :cleanup do
  on release_roles :all do |host|
    releases = capture(:ls, '-xtr', releases_path).split
    if releases.count >= fetch(:keep_releases)
      info t(:keeping_releases, host: host.to_s, keep_releases: fetch(:keep_releases), releases: releases.count)
      directories = (releases - releases.last(fetch(:keep_releases)))
      if directories.any?
        directories_str = directories.map do |release|
          releases_path.join(release)
        end.join(" ")
        execute :rm, '-rf', directories_str
      else
        info t(:no_old_releases, host: host.to_s, keep_releases: fetch(:keep_releases))
      end
    end
  end
end

desc 'Remove and archive rolled-back release.'
task :cleanup_rollback do
  on release_roles(:all) do
    last_release = capture(:ls, '-xt', releases_path).split.first
    last_release_path = releases_path.join(last_release)
    if test "[ `readlink #{current_path}` != #{last_release_path} ]"
      execute :tar, '-czf',
        deploy_path.join("rolled-back-release-#{last_release}.tar.gz"),
      last_release_path
      execute :rm, '-rf', last_release_path
    else
      debug 'Last release is the current release, skip cleanup_rollback.'
    end
  end
end

desc 'Log details of the deploy'
task :log_revision do
  on release_roles(:all) do
    within releases_path do
      execute %{echo "#{revision_log_message}" >> #{revision_log}}
    end
  end
end

desc 'Revert to previous release timestamp'
task :revert_release => :rollback_release_path do
  on release_roles(:all) do
    set(:revision_log_message, rollback_log_message)
  end
end

task :new_release_path do
  set_release_path
end

task :rollback_release_path do
  on release_roles(:all) do
    releases = capture(:ls, '-xt', releases_path).split
    if releases.count < 2
      error t(:cannot_rollback)
      exit 1
    end
    last_release = releases[1]
    set_release_path(last_release)
    set(:rollback_timestamp, last_release)
  end
end

desc "Place a REVISION file with the current revision SHA in the current release path"
task :set_current_revision  do
  invoke "#{scm}:set_current_revision"
  on release_roles(:all) do
    within release_path do
      execute :echo, "\"#{fetch(:current_revision)}\" >> REVISION"
    end
  end
end

task :set_previous_revision do
  on release_roles(:all) do
    target = release_path.join('REVISION')
    if test "[ -f #{target} ]"
      set(:previous_revision, capture(:cat, target, '2>/dev/null'))
    end
  end
end

task :restart
task :failed

end