# frozen_string_literal: true

module Integrations
  module Propagation
    class BulkUpdateService
      include BulkOperationHashes

      def initialize(integration, batch)
        @integration = integration
        @batch = batch
      end

      # rubocop: disable CodeReuse/ActiveRecord
      def execute
        Integration.transaction do
          Integration.where(id: batch_ids).update_all(integration_hash(:update))

          if integration.data_fields_present?
            integration.data_fields.class.where(data_fields_foreign_key => batch_ids)
              .update_all(
                data_fields_hash(:update)
              )
          end

          if integration.is_a?(GitlabSlackApplication)
            if integration.active?
              bulk_upsert_slack_integrations
            else
              bulk_delete_slack_integrations
            end
          end
        end
      end
      # rubocop: enable CodeReuse/ActiveRecord

      private

      attr_reader :integration, :batch

      # service_id or integration_id
      def data_fields_foreign_key
        integration.data_fields.class.reflections['integration'].foreign_key
      end

      def batch_ids
        @batch_ids ||=
          if batch.is_a?(ActiveRecord::Relation)
            batch.select(:id)
          else
            batch.map(&:id)
          end
      end

      def bulk_upsert_slack_integrations
        bulk_update_slack_integrations

        inserted_slack_ids = bulk_insert_slack_integrations
        bulk_insert_slack_integration_scopes(inserted_slack_ids)
      end

      def bulk_update_slack_integrations
        slack_integration_batch = SlackIntegration.by_integration(batch_ids)

        return unless slack_integration_batch.present?

        slack_integration_batch.update_all(
          integration.slack_integration.to_database_hash
        )

        Integrations::SlackWorkspace::IntegrationApiScope.update_scopes(
          slack_integration_batch,
          integration.slack_integration.slack_api_scopes
        )
      end

      def bulk_insert_slack_integrations
        integrations_missing_slack_integration = Integrations::GitlabSlackApplication
          .by_ids(batch_ids)
          .without_slack_integration

        return [] unless integrations_missing_slack_integration.present?

        db_hash = integration.slack_integration.to_database_hash
        items_to_insert = integrations_missing_slack_integration.map do |integration_record|
          db_hash.merge(
            'integration_id' => integration_record.id,
            'alias' => integration_record.parent.full_path,
            'organization_id' => integration_record.organization_id,
            'group_id' => integration_record.group_id,
            'project_id' => integration_record.project_id
          )
        end

        bulk_insert_new(SlackIntegration, items_to_insert)
      end

      def bulk_insert_slack_integration_scopes(inserted_slack_ids)
        return unless inserted_slack_ids.present?

        # TODO: Remove finding by name once the backfill has been finalized
        # https://gitlab.com/gitlab-org/gitlab/-/issues/560356
        scope_names = integration.slack_integration.slack_api_scopes.map(&:name)
        scopes = SlackWorkspace::ApiScope.find_or_initialize_by_names(
          scope_names, organization_id: integration.organization_id_from_parent
        )

        items_to_insert = SlackIntegration.id_in(inserted_slack_ids)
                                          .preloaded_integration
                                          .flat_map do |slack_integration|
          scopes.map do |scope|
            {
              'slack_integration_id' => slack_integration.id,
              'slack_api_scope_id' => scope.id,
              'project_id' => slack_integration.integration.project_id,
              'group_id' => slack_integration.integration.group_id,
              'organization_id' => slack_integration.integration.organization_id
            }
          end
        end

        bulk_insert_new(SlackWorkspace::IntegrationApiScope, items_to_insert)
      end

      def bulk_delete_slack_integrations
        SlackIntegration.by_integration(batch_ids).delete_all
      end
    end
  end
end
