require "paperclip/attachment_registry"

module Paperclip
  module Task
    def self.obtain_class
      class_name = ENV["CLASS"] || ENV["class"]
      raise "Must specify CLASS" unless class_name

      class_name
    end

    def self.obtain_attachments(klass)
      klass = Paperclip.class_for(klass.to_s)
      name = ENV["ATTACHMENT"] || ENV["attachment"]

      attachment_names = Paperclip::AttachmentRegistry.names_for(klass)

      raise "Class #{klass.name} has no attachments specified" if attachment_names.empty?

      if name.present? && attachment_names.map(&:to_s).include?(name.to_s)
        [name]
      else
        attachment_names
      end
    end

    def self.log_error(error)
      $stderr.puts error
    end
  end
end

namespace :paperclip do
  desc "Refreshes both metadata and thumbnails."
  task refresh: ["paperclip:refresh:metadata", "paperclip:refresh:thumbnails"]

  namespace :refresh do
    desc "Regenerates thumbnails for a given CLASS (and optional ATTACHMENT and STYLES splitted by comma)."
    task thumbnails: :environment do
      klass = Paperclip::Task.obtain_class
      names = Paperclip::Task.obtain_attachments(klass)
      styles = (ENV["STYLES"] || ENV["styles"] || "").split(",").map(&:to_sym)
      names.each do |name|
        Paperclip.each_instance_with_attachment(klass, name) do |instance|
          attachment = instance.send(name)
          begin
            attachment.reprocess!(*styles)
          rescue StandardError => e
            Paperclip::Task.log_error("exception while processing #{klass} ID #{instance.id}:")
            Paperclip::Task.log_error(" " + e.message + "\n")
          end
          unless instance.errors.blank?
            Paperclip::Task.log_error("errors while processing #{klass} ID #{instance.id}:")
            Paperclip::Task.log_error(" " + instance.errors.full_messages.join("\n ") + "\n")
          end
        end
      end
    end

    desc "Regenerates content_type/size metadata for a given CLASS (and optional ATTACHMENT)."
    task metadata: :environment do
      klass = Paperclip::Task.obtain_class
      names = Paperclip::Task.obtain_attachments(klass)
      names.each do |name|
        Paperclip.each_instance_with_attachment(klass, name) do |instance|
          attachment = instance.send(name)
          if file = Paperclip.io_adapters.for(attachment, attachment.options[:adapter_options])
            instance.send("#{name}_file_name=", instance.send("#{name}_file_name").strip)
            instance.send("#{name}_content_type=", file.content_type.to_s.strip)
            instance.send("#{name}_file_size=", file.size) if instance.respond_to?("#{name}_file_size")
            instance.save(validate: false)
          else
            true
          end
        end
      end
    end

    desc "Regenerates missing thumbnail styles for all classes using Paperclip."
    task missing_styles: :environment do
      Rails.application.eager_load!
      Paperclip.missing_attachments_styles.each do |klass, attachment_definitions|
        attachment_definitions.each do |attachment_name, missing_styles|
          puts "Regenerating #{klass} -> #{attachment_name} -> #{missing_styles.inspect}"
          ENV["CLASS"] = klass.to_s
          ENV["ATTACHMENT"] = attachment_name.to_s
          ENV["STYLES"] = missing_styles.join(",")
          Rake::Task["paperclip:refresh:thumbnails"].execute
        end
      end
      Paperclip.save_current_attachments_styles!
    end

    desc "Regenerates fingerprints for a given CLASS (and optional ATTACHMENT). Useful when changing digest."
    task fingerprints: :environment do
      klass = Paperclip::Task.obtain_class
      names = Paperclip::Task.obtain_attachments(klass)
      names.each do |name|
        Paperclip.each_instance_with_attachment(klass, name) do |instance|
          attachment = instance.send(name)
          attachment.assign(attachment)
          instance.save(validate: false)
        end
      end
    end
  end

  desc "Cleans out invalid attachments. Useful after you've added new validations."
  task clean: :environment do
    klass = Paperclip::Task.obtain_class
    names = Paperclip::Task.obtain_attachments(klass)
    names.each do |name|
      Paperclip.each_instance_with_attachment(klass, name) do |instance|
        unless instance.valid?
          attributes = %w(file_size file_name content_type).map { |suffix| "#{name}_#{suffix}".to_sym }
          if attributes.any? { |attribute| instance.errors[attribute].present? }
            instance.send("#{name}=", nil)
            instance.save(validate: false)
          end
        end
      end
    end
  end

  desc "find missing attachments. Useful to know which attachments are broken"
  task find_broken_attachments: :environment do
    klass = Paperclip::Task.obtain_class
    names = Paperclip::Task.obtain_attachments(klass)
    names.each do |name|
      Paperclip.each_instance_with_attachment(klass, name) do |instance|
        attachment = instance.send(name)
        if attachment.exists?
          print "."
        else
          Paperclip::Task.log_error("#{instance.class}##{attachment.name}, #{instance.id}, #{attachment.url}")
        end
      end
    end
  end
end
