# frozen_string_literal: true

module Todos
  module Destroy
    class EntityLeaveService < ::Todos::Destroy::BaseService
      extend ::Gitlab::Utils::Override

      attr_reader :user, :entity

      # rubocop: disable CodeReuse/ActiveRecord
      def initialize(user_id, entity_id, entity_type)
        unless %w(Group Project).include?(entity_type)
          raise ArgumentError.new("#{entity_type} is not an entity user can leave")
        end

        @user = User.find_by(id: user_id)
        @entity = entity_type.constantize.find_by(id: entity_id)
      end
      # rubocop: enable CodeReuse/ActiveRecord

      def execute
        return unless entity && user

        # if at least reporter, all entities including confidential issues can be accessed
        return if user_has_reporter_access?

        remove_confidential_issue_todos

        if entity.private?
          remove_project_todos
          remove_group_todos
        else
          enqueue_private_features_worker
        end
      end

      private

      def enqueue_private_features_worker
        project_ids.each do |project_id|
          TodosDestroyer::PrivateFeaturesWorker.perform_async(project_id, user.id)
        end
      end

      # rubocop: disable CodeReuse/ActiveRecord
      def remove_confidential_issue_todos
        Todo.where(
          target_id: confidential_issues.select(:id), target_type: Issue, user_id: user.id
        ).delete_all
      end
      # rubocop: enable CodeReuse/ActiveRecord

      # rubocop: disable CodeReuse/ActiveRecord
      def remove_project_todos
        Todo.where(project_id: non_authorized_projects, user_id: user.id).delete_all
      end
      # rubocop: enable CodeReuse/ActiveRecord

      # rubocop: disable CodeReuse/ActiveRecord
      def remove_group_todos
        Todo.where(group_id: non_authorized_groups, user_id: user.id).delete_all
      end
      # rubocop: enable CodeReuse/ActiveRecord

      override :project_ids
      # rubocop: disable CodeReuse/ActiveRecord
      def project_ids
        condition = case entity
                    when Project
                      { id: entity.id }
                    when Namespace
                      { namespace_id: non_member_groups }
                    end

        Project.where(condition).select(:id)
      end
      # rubocop: enable CodeReuse/ActiveRecord

      # rubocop: disable CodeReuse/ActiveRecord
      def non_authorized_projects
        project_ids.where('id NOT IN (?)', user.authorized_projects.select(:id))
      end
      # rubocop: enable CodeReuse/ActiveRecord

      # rubocop: disable CodeReuse/ActiveRecord
      def non_authorized_groups
        return [] unless entity.is_a?(Namespace)

        entity.self_and_descendants.select(:id)
          .where('id NOT IN (?)', GroupsFinder.new(user).execute.select(:id))
      end
      # rubocop: enable CodeReuse/ActiveRecord

      # rubocop: disable CodeReuse/ActiveRecord
      def non_member_groups
        entity.self_and_descendants.select(:id)
          .where('id NOT IN (?)', user.membership_groups.select(:id))
      end
      # rubocop: enable CodeReuse/ActiveRecord

      def user_has_reporter_access?
        return unless entity.is_a?(Namespace)

        entity.member?(User.find(user.id), Gitlab::Access::REPORTER)
      end

      # rubocop: disable CodeReuse/ActiveRecord
      def confidential_issues
        assigned_ids = IssueAssignee.select(:issue_id).where(user_id: user.id)
        authorized_reporter_projects = user
          .authorized_projects(Gitlab::Access::REPORTER).select(:id)

        Issue.where(project_id: project_ids, confidential: true)
          .where('project_id NOT IN(?)', authorized_reporter_projects)
          .where('author_id != ?', user.id)
          .where('id NOT IN (?)', assigned_ids)
      end
      # rubocop: enable CodeReuse/ActiveRecord
    end
  end
end
