# frozen_string_literal: true

# rubocop:disable Style/MixinUsage include Comparable # rubocop:enable Style/MixinUsage

# rubocop:disable Metrics/BlockLength namespace :load do

task :defaults do
  # Which roles to consider as laravel roles
  set :laravel_roles, :all

  # The artisan flags to include on artisan commands by default
  set :laravel_artisan_flags, "--env=#{fetch(:stage)}"

  # Which roles to use for running migrations
  set :laravel_migration_roles, :all

  # The artisan flags to include on commands when running migrations
  set :laravel_migration_artisan_flags, "--force --env=#{fetch(:stage)}"

  # The version of laravel being deployed
  set :laravel_version, 5.3

  # Whether to upload the dotenv file on deploy
  set :laravel_upload_dotenv_file_on_deploy, true

  # Which dotenv file to transfer to the server
  set :laravel_dotenv_file, '.env'

  # The user that the server is running under (used for ACLs)
  set :laravel_server_user, 'www-data'

  # Ensure the dirs in :linked_dirs exist?
  set :laravel_ensure_linked_dirs_exist, true

  # Link the directores in laravel_linked_dirs?
  set :laravel_set_linked_dirs, true

  # Linked directories for a standard Laravel 4 application
  set :laravel_4_linked_dirs, [
    'app/storage/public',
    'app/storage/cache',
    'app/storage/logs',
    'app/storage/meta',
    'app/storage/sessions',
    'app/storage/views'
  ]

  # Linked directories for a standard Laravel 5 application
  set :laravel_5_linked_dirs, [
    'storage/app',
    'storage/framework/cache',
    'storage/framework/sessions',
    'storage/framework/views',
    'storage/logs'
  ]

  # Ensure the paths in :file_permissions_paths exist?
  set :laravel_ensure_acl_paths_exist, true

  # Set ACLs for the paths in laravel_acl_paths?
  set :laravel_set_acl_paths, true

  # Paths that should have ACLs set for a standard Laravel 4 application
  set :laravel_4_acl_paths, [
    'app/storage',
    'app/storage/public',
    'app/storage/cache',
    'app/storage/logs',
    'app/storage/meta',
    'app/storage/sessions',
    'app/storage/views'
  ]

  # Paths that should have ACLs set for a standard Laravel 5 application
  set :laravel_5_acl_paths, [
    'bootstrap/cache',
    'storage',
    'storage/app',
    'storage/app/public',
    'storage/framework',
    'storage/framework/cache',
    'storage/framework/sessions',
    'storage/framework/views',
    'storage/logs'
  ]
end

end # rubocop:enable Metrics/BlockLength

# rubocop:disable Metrics/BlockLength namespace :laravel do

desc 'Determine which folders, if any, to use for linked directories.'
task :resolve_linked_dirs do
  laravel_version = fetch(:laravel_version)

  # Use Laravel 5 linked dirs by default
  laravel_linked_dirs = fetch(:laravel_5_linked_dirs)

  # Laravel 4
  laravel_linked_dirs = fetch(:laravel_4_linked_dirs) if laravel_version < 5

  if fetch(:laravel_set_linked_dirs)
    set :linked_dirs, fetch(:linked_dirs, []).push(*laravel_linked_dirs)
  end
end

desc 'Determine which paths, if any, to have ACL permissions set.'
task :resolve_acl_paths do
  next unless fetch(:laravel_set_acl_paths)

  laravel_version = fetch(:laravel_version)

  # Use Laravel 5 ACL paths by default
  laravel_acl_paths = fetch(:laravel_5_acl_paths)

  # Laravel 4
  laravel_acl_paths = fetch(:laravel_4_acl_paths) if laravel_version < 5

  set :file_permissions_paths, fetch(:file_permissions_paths, [])
    .push(*laravel_acl_paths)
    .uniq
  set :file_permissions_users, fetch(:file_permissions_users, [])
    .push(fetch(:laravel_server_user))
    .uniq
end

desc 'Ensure that linked dirs exist.'
task :ensure_linked_dirs_exist do
  next unless fetch(:laravel_ensure_linked_dirs_exist)

  on roles fetch(:laravel_roles) do
    fetch(:linked_dirs).each do |path|
      within shared_path do
        execute :mkdir, '-p', path
      end
    end
  end
end

desc 'Ensure that ACL paths exist.'
task :ensure_acl_paths_exist do
  next unless fetch(:laravel_set_acl_paths) &&
              fetch(:laravel_ensure_acl_paths_exist)

  on roles fetch(:laravel_roles) do
    fetch(:file_permissions_paths).each do |path|
      within release_path do
        execute :mkdir, '-p', path
      end
    end
  end
end

desc 'Upload dotenv file for release.'
task :upload_dotenv_file do
  next unless fetch(:laravel_upload_dotenv_file_on_deploy)

  # Dotenv was introduced in Laravel 5
  next if fetch(:laravel_version) < 5

  dotenv_file = fetch(:laravel_dotenv_file)

  run_locally do
    if dotenv_file.empty? || test("[ ! -e #{dotenv_file} ]")
      raise Capistrano::ValidationError,
            "Must prepare dotenv file [#{dotenv_file}] locally before deploy!"
    end
  end

  on roles fetch(:laravel_roles) do
    upload! dotenv_file, "#{release_path}/.env"
  end
end

desc 'Execute a provided artisan command.'
task :artisan, [:command_name] do |_t, args|
  ask(:cmd, 'list') # Ask only runs if argument is not provided
  command = args[:command_name] || fetch(:cmd)

  on roles fetch(:laravel_roles) do
    within release_path do
      execute :php,
              :artisan,
              command,
              *args.extras,
              fetch(:laravel_artisan_flags)
    end
  end

  # Enable task artisan to be ran more than once
  Rake::Task['laravel:artisan'].reenable
end

desc 'Create a cache file for faster configuration loading.'
task :config_cache do
  next if fetch(:laravel_version) < 5

  Rake::Task['laravel:artisan'].invoke('config:cache')
end

desc 'Create a route cache file for faster route registration.'
task :route_cache do
  next if fetch(:laravel_version) < 5

  Rake::Task['laravel:artisan'].invoke('route:cache')
end

desc 'Optimize the framework for better performance.'
task :optimize do
  next if fetch(:laravel_version) >= 5.5

  Rake::Task['laravel:artisan'].invoke(:optimize, '--force')
end

desc 'Create a symbolic link from "public/storage" to "storage/app/public."'
task :storage_link do
  next if fetch(:laravel_version) < 5.3

  Rake::Task['laravel:artisan'].invoke('storage:link')
end

desc 'Run the database migrations.'
task :migrate do
  laravel_roles = fetch(:laravel_roles)
  laravel_artisan_flags = fetch(:laravel_artisan_flags)

  set(:laravel_roles, fetch(:laravel_migration_roles))
  set(:laravel_artisan_flags, fetch(:laravel_migration_artisan_flags))

  Rake::Task['laravel:artisan'].invoke(:migrate)

  set(:laravel_roles, laravel_roles)
  set(:laravel_artisan_flags, laravel_artisan_flags)
end

desc 'Rollback the last database migration.'
task :migrate_rollback do
  laravel_roles = fetch(:laravel_roles)
  laravel_artisan_flags = fetch(:laravel_artisan_flags)

  set(:laravel_roles, fetch(:laravel_migration_roles))
  set(:laravel_artisan_flags, fetch(:laravel_migration_artisan_flags))

  Rake::Task['laravel:artisan'].invoke('migrate:rollback')

  set(:laravel_roles, laravel_roles)
  set(:laravel_artisan_flags, laravel_artisan_flags)
end

before 'deploy:starting', 'laravel:resolve_linked_dirs'
before 'deploy:starting', 'laravel:resolve_acl_paths'
after  'deploy:starting', 'laravel:ensure_linked_dirs_exist'
after  'deploy:updating', 'laravel:ensure_acl_paths_exist'
before 'deploy:updated',  'deploy:set_permissions:acl'
before 'deploy:updated',  'laravel:upload_dotenv_file'
after  'composer:run',    'laravel:storage_link'
after  'composer:run',    'laravel:optimize'

end # rubocop:enable Metrics/BlockLength