# frozen_string_literal: true

require 'caffeinate/drip_evaluator'
require 'caffeinate/schedule_evaluator'

module Caffeinate
  # A Drip object
  #
  # Handles the block and provides convenience methods for the drip
  class Drip
    ALL_DRIP_OPTIONS = [:mailer_class, :mailer, :start, :using, :step]
    VALID_DRIP_OPTIONS = ALL_DRIP_OPTIONS + [:delay, :start, :at, :on].freeze

    class << self
      def build(dripper, action, options, &block)
        options = options.with_defaults(dripper.defaults)
        validate_drip_options(dripper, action, options)

        new(dripper, action, options, &block)
      end

      private

      def validate_drip_options(dripper, action, options)
        options = normalize_options(dripper, options)

        if options[:mailer_class].nil? && options[:action_class].nil?
          raise ArgumentError, "You must define :mailer_class, :mailer, or :action_class in the options for #{action.inspect} on #{dripper.inspect}"
        end

        if options[:every].nil? && options[:delay].nil? && options[:on].nil?
          raise ArgumentError, "You must define :delay or :on or :every in the options for #{action.inspect} on #{dripper.inspect}"
        end

        options
      end

      def normalize_options(dripper, options)
        options[:mailer_class] ||= options[:mailer] || dripper.defaults[:mailer_class]
        options[:using] ||= dripper.defaults[:using]
        options[:step] ||= dripper.drips.size + 1

        options
      end
    end

    attr_reader :dripper, :action, :options, :block

    def initialize(dripper, action, options, &block)
      @dripper = dripper
      @action = action
      @options = options
      @block = block
    end

    # If the associated ActionMailer uses `ActionMailer::Parameterized` initialization instead of argument-based initialization
    def parameterized?
      options[:using] == :parameterized
    end

    def send_at(mailing = nil)
      ::Caffeinate::ScheduleEvaluator.call(self, mailing)
    end

    # Checks if the drip is enabled
    #
    # This is kind of messy and could use some love.
    # todo: better.
    def enabled?(mailing)
      catch(:abort) do
        if dripper.run_callbacks(:before_drip, self, mailing)
          return DripEvaluator.new(mailing).call(&@block)
        else
          return false
        end
      end
      false
    end

    # allows for hitting type.periodical? or type.drip?
    def type
      name = self.class.name.demodulize.delete_suffix("Drip").presence || "Drip"

      ActiveSupport::StringInquirer.new(name.downcase)
    end
  end
end
