# Beta invitations
# http://railscasts.com/episodes/124-beta-invitations
class Invitation < ApplicationRecord
  belongs_to :creator, polymorphic: true
  belongs_to :invitee, polymorphic: true
  belongs_to :external_author

  validate :recipient_is_not_registered, on: :create
  def recipient_is_not_registered
    # we allow invitations to be sent to existing users if the purpose is to claim an external author
    if self.invitee_email && User.find_by(email: self.invitee_email) && !self.external_author
      errors.add :invitee_email, ts('is already being used by an account holder.')
    end
  end

  # ensure email is valid
  validates :invitee_email, email_format: true, allow_blank: true

  scope :unsent, -> { where(invitee_email: nil, redeemed_at: nil) }
  scope :unredeemed, -> { where('invitee_email IS NOT NULL and redeemed_at IS NULL') }
  scope :redeemed, -> { where('redeemed_at IS NOT NULL') }
  scope :from_queue, -> { where(external_author: nil).where(creator_type: [nil, "Admin"]) }

  before_validation :generate_token, on: :create
  after_save :send_and_set_date, if: :saved_change_to_invitee_email?
  after_save :adjust_user_invite_status

  #Create a certain number of invitations for all valid users
  def self.grant_all(total)
    raise unless total > 0 && total < 20
    User.valid.each do |user|
      total.times do
        user.invitations.create
      end
      I18n.with_locale(user.preference.locale_for_mails) do
        UserMailer.invite_increase_notification(user.id, total).deliver_later
      end
    end
    User.out_of_invites.update_all('out_of_invites = 0')
  end

  #Create a certain number of invitations for all users who are out of them
  def self.grant_empty(total)
    raise unless total > 0 && total < 20
    User.valid.out_of_invites.each do |user|
      total.times do
        user.invitations.create
      end
      I18n.with_locale(user.preference.locale_for_mails) do
        UserMailer.invite_increase_notification(user.id, total).deliver_later
      end
    end
    User.out_of_invites.update_all('out_of_invites = 0')
  end

  def mark_as_redeemed(user=nil)
    self.invitee = user
    self.redeemed_at = Time.now
    save
  end

  def send_and_set_date(resend: false)
    return if invitee_email.blank?

    if self.external_author
      archivist = self.external_author.external_creatorships.collect(&:archivist).collect(&:login).uniq.join(", ")
      # send invite synchronously for now -- this should now work delayed but just to be safe
      UserMailer.invitation_to_claim(self.id, archivist).deliver_now
    else
      # send invitations actively sent by a user synchronously to avoid delays
      UserMailer.invitation(self.id).deliver_now
    end

    # Skip callbacks within after_save by using update_column to avoid a callback loop
    if resend
      attrs = { resent_at: Time.current }
      # This applies to old invites when AO3-6094 wasn't fixed.
      attrs[:sent_at] = self.created_at if self.sent_at.nil?
      self.update_columns(attrs)
    else
      self.update_column(:sent_at, Time.current)
    end
  rescue StandardError => e
    errors.add(:base, :notification_could_not_be_sent, error: e.message)
  end

  def can_resend?
    # created_at fallback is a vestige of the already fixed AO3-6094.
    checked_date = self.resent_at || self.sent_at || self.created_at
    checked_date < ArchiveConfig.HOURS_BEFORE_RESEND_INVITATION.hours.ago
  end

  private

  def generate_token
    self.token = Digest::SHA1.hexdigest([Time.current, rand].join)
  end

  #Update the user's out_of_invites status
  def adjust_user_invite_status
    if self.creator.respond_to?(:out_of_invites)
      self.creator.out_of_invites = (self.creator.invitations.unredeemed.count < 1)
      self.creator.save!(validate: false)
    end
  end
end
