class Kudo < ApplicationRecord
  include Responder

  VALID_COMMENTABLE_TYPES = %w[Work].freeze

  belongs_to :user
  belongs_to :commentable, polymorphic: true

  validates :commentable_type, inclusion: { in: VALID_COMMENTABLE_TYPES }
  validates :commentable,
            presence: true,
            if: proc { |c| VALID_COMMENTABLE_TYPES.include?(c.commentable_type) }

  validates :user, not_blocked: { by: :commentable, on: :create }

  validate :cannot_be_author, on: :create
  def cannot_be_author
    return unless user&.is_author_of?(commentable)

    errors.add(:commentable, :author_on_own_work)
  end

  validate :guest_cannot_kudos_restricted_work, on: :create
  def guest_cannot_kudos_restricted_work
    return unless user.blank? && commentable.is_a?(Work) && commentable.restricted?

    errors.add(:commentable, :guest_on_restricted)
  end

  validate :cannot_be_suspended, on: :create
  def cannot_be_suspended
    return unless user&.banned || user&.suspended

    if user.banned
      errors.add(:commentable, :user_is_banned)
    else
      errors.add(:commentable, :user_is_suspended)
    end
  end

  validates :ip_address,
            uniqueness: { scope: [:commentable_id, :commentable_type] },
            if: proc { |kudo| kudo.ip_address.present? }

  validates :user_id,
            uniqueness: { scope: [:commentable_id, :commentable_type] },
            if: proc { |kudo| kudo.user.present? }

  validate :cannot_be_official_user, on: :create
  def cannot_be_official_user
    return unless user&.official

    errors.add(:user, :official)
  end

  validate :cannot_be_archivist_account, on: :create
  def cannot_be_archivist_account
    return unless user&.archivist

    errors.add(:user, :archivist)
  end

  scope :with_user, -> { where("user_id IS NOT NULL") }
  scope :by_guest, -> { where("user_id IS NULL") }

  after_destroy :update_work_stats
  after_create :after_create, :update_work_stats
  def after_create
    users = self.commentable.pseuds.map(&:user).uniq

    users.each do |user|
      if notify_user_by_email?(user)
        RedisMailQueue.queue_kudo(user, self)
      end
    end
  end

  after_save :expire_caches
  def expire_caches
    if commentable_type == "Work"
      # Expire the work's cached total kudos count.
      Rails.cache.delete("works/#{commentable_id}/kudos_count-v2")
      # If it's a guest kudo, also expire the work's cached guest kudos count.
      Rails.cache.delete("works/#{commentable_id}/guest_kudos_count-v2") if user_id.nil?
    end

    # Expire the cached kudos section under the work.
    ActionController::Base.new.expire_fragment("#{commentable.cache_key}/kudos-v4")
  end

  def notify_user_by_email?(user)
    user.nil? ? false : ( user.is_a?(Admin) ? true :
      !(user == User.orphan_account || user.preference.kudos_emails_off?) )
  end

  # Return either the name of the kudo-giver or "guest".
  # Used in kudo notifications.
  def name
    if self.user
      user.login
    else
      "guest"
    end
  end
end
