module ChallengeCore

  # Used to ensure allowed is no less than required
  def update_allowed_values
    self.class::PROMPT_TYPES.each do |prompt_type|
      required = required(prompt_type)
      eval("#{prompt_type}_num_allowed = required") if required > allowed(prompt_type)
    end
  end

  # make sure that challenge sign-up dates / open dates aren't contradictory
  def validate_signup_dates
    # some variables for clarity
    error_message = []
    open_date  = self.signups_open_at
    close_date = self.signups_close_at
    signups_open = self.signup_open
    if signups_open
      if close_date && close_date.past?
        error_message << ts("If sign-ups are open, sign-up close date cannot be in the past.")
      end
      if open_date && open_date.future?
        error_message << ts("If sign-ups are open, sign-up open date cannot be in the future.")
      end
      # rubocop:disable Style/IfUnlessModifier
      if close_date && open_date && close_date.to_fs(:number) < open_date.to_fs(:number)
        error_message << ts("Close date cannot be before open date.")
      end
      # rubocop:enable Style/IfUnlessModifier
    end
    unless error_message.empty?
      error_message.each do |errors|
        self.errors.add(:base, errors)
      end
    end
  end

  # When Challenges are deleted, there are two references left behind that need to be reset to nil
  def clear_challenge_references
    collection.challenge_id = nil
    collection.challenge_type = nil
    collection.save!
  end
  
  # a couple of handy shorthand methods
  def required(type)
    self.send("#{type}_num_required")
  end
  
  def allowed(type)
    self.send("#{type}_num_allowed")
  end

  def allowed_range_string(type)
    "#{required(type)}" + (allowed(type) != required(type) ? " - #{allowed(type)}" : '')
  end

  #### Management 
  
  def user_allowed_to_see_signups?(user)
    self.collection.user_is_maintainer?(user)
  end
  
  def user_allowed_to_see_assignments?(user)
    self.collection.user_is_maintainer?(user)
  end

  def user_allowed_to_sign_up?(user)
    self.collection.user_is_maintainer?(user) || self.signup_open
  end
  
  def user_allowed_to_see_prompt?(user, prompt)
    true
  end

  # whether users can change the name on their signup or not -- override in challenge class as appropriate
  def allow_name_change?
    true
  end

  def should_update_collection_index?
    return true if destroyed?

    pertinent_attributes = %w[signup_open signups_open_at signups_close_at assignments_due_at works_reveal_at authors_reveal_at]
    (self.saved_changes.keys & pertinent_attributes).present?
  end

  def update_collection_index
    return if self.collection.blank?

    IndexQueue.enqueue_id(Collection, collection.id, :main)
  end

  module ClassMethods
    # override datetime setters so we can take strings
    def override_datetime_setters
      %w(signups_open_at signups_close_at assignments_due_at works_reveal_at authors_reveal_at).each do |datetime_attr|
        define_method("#{datetime_attr}_string") do
          return unless (datetime = self[datetime_attr])

          datetime.in_time_zone(time_zone.presence || Time.zone).strftime(ArchiveConfig.DEFAULT_DATETIME_FORMAT)
        end

        define_method("#{datetime_attr}_string=") do |datetimestring|
          self[datetime_attr] = Timeliness.parse(datetimestring, zone: (time_zone.presence || Time.zone))
        end
      end
    end
  end
  
  def self.included(base)
    base.class_eval do
      after_commit :update_collection_index, if: :should_update_collection_index?
    end
    base.extend(ClassMethods)
  end
  
end
