class Labels::DestroyService
  pattr_initialize [:label_title!, :account_id!, :label_deleted_at!]

  def perform
    conversation_labels_removed = remove_conversation_labels
    remove_contact_labels
    invalidate_filtered_unread_count_conversations if conversation_labels_removed
  end

  private

  def remove_conversation_labels
    conversation_labels_removed = false

    tagged_conversations.find_in_batches do |conversation_batch|
      conversation_batch.each do |conversation|
        update_conversation_cached_labels(conversation)
      end
      delete_label_taggings('Conversation', conversation_batch.map(&:id))
      conversation_labels_removed = true
    end

    conversation_labels_removed
  end

  def remove_contact_labels
    contact_label_taggings.in_batches do |tagging_batch|
      ActsAsTaggableOn::Tagging.where(id: tagging_batch.select(:id)).delete_all
    end
  end

  def update_conversation_cached_labels(conversation)
    label_list = conversation.label_list.dup
    label_list.remove(label_title)

    # We only want the acts-as-taggable-on cache effect here, not Conversation callbacks/events.
    # rubocop:disable Rails/SkipsModelValidations
    conversation.update_column(:cached_label_list, label_list.join("#{ActsAsTaggableOn.delimiter} "))
    # rubocop:enable Rails/SkipsModelValidations
  end

  def tagged_conversations
    account.conversations.where(id: label_taggings_for('Conversation').select(:taggable_id))
  end

  def contact_label_taggings
    label_taggings_for('Contact').where(taggable_id: account.contacts.select(:id))
  end

  def delete_label_taggings(taggable_type, taggable_ids)
    ActsAsTaggableOn::Tagging
      .where(id: label_taggings_for(taggable_type).where(taggable_id: taggable_ids).select(:id))
      .delete_all
  end

  def label_taggings_for(taggable_type)
    ActsAsTaggableOn::Tagging
      .joins(:tag)
      .where(context: 'labels', taggable_type: taggable_type, tags: { name: label_title })
      .where('taggings.created_at <= ?', label_deleted_at)
  end

  def account
    @account ||= Account.find(account_id)
  end

  def invalidate_filtered_unread_count_conversations
    ::Conversations::UnreadCounts::FilteredCountInvalidator.new(account).conversation_changed!
  end
end
