namespace :google_authenticator do

  def do_encrypt(args, already_encrypted, op_name)
    model_names = if args[:optional_model_list]
                    args.extras.unshift(args[:optional_model_list])
                  else
                    # Adapted from https://stackoverflow.com/a/8248849/7478194
                    Dir[Rails.root.join('app/models/*.rb').to_s].map { |filename| File.basename(filename, '.rb').camelize }
                  end

    ActiveRecord::Base.transaction do
      match_op = already_encrypted ? " in (138,162)" : "in (16,32)"
      model_names.each do |model_name|
        klass = model_name.constantize
        next unless klass.ancestors.include?(ActiveRecord::Base) && klass.try(:google_secrets_encrypted)
        print "#{op_name}ing model #{klass.name.inspect} (table #{klass.table_name.inspect}): "
        count = 0
        klass.where("LENGTH(#{klass.google_secret_column})#{match_op}").find_each do |record|
          yield record
          count += 1
        end
        puts "#{count} #{'secret'.pluralize(count)} #{op_name}ed"
      end
    end
  end

  desc 'Encrypt all secret columns (add the :encrypt_secrets options *before* running)'
  task :encrypt_secrets, [:optional_model_list] => :environment do |_t, args|
    do_encrypt(args, false, 'Encrypt') { |record| record.encrypt_google_secret! }
  end

  desc 'Re-encrypt all secret columns from old_secret_key_base to secret_key_base'
  task :reencrypt_secrets, [:optional_model_list] => :environment do |_t, args|
    if Rails.application.secrets.old_secret_key_base.blank?
      puts 'old_secret_key_base is not set in config/secrets.yml'
    else
      secret_encryptor = GoogleAuthenticatorRails::ActiveRecord::Helpers.get_google_secret_encryptor
      Rails.application.secrets[:secret_key_base] = Rails.application.secrets.old_secret_key_base
      Rails.application.instance_eval { @caching_key_generator = nil }
      old_secret_encryptor = GoogleAuthenticatorRails::ActiveRecord::Helpers.get_google_secret_encryptor
      do_encrypt(args, true, 'Re-encrypt') do |record|
        GoogleAuthenticatorRails.secret_encryptor = old_secret_encryptor
        plain_secret = record.google_secret_value
        GoogleAuthenticatorRails.secret_encryptor = secret_encryptor
        record.send(:change_google_secret_to!, plain_secret)
      end
    end
  end

  desc 'Decrypt all secret columns (remove the :encrypt_secrets options *after* running)'
  task :decrypt_secrets, [:optional_model_list] => :environment do |_t, args|
    do_encrypt(args, true, 'Decrypt') { |record| record.send(:change_google_secret_to!, record.google_secret_value, false) }
  end

end
