# frozen_string_literal: true

module Integrations
  module BaseDataFields
    extend ActiveSupport::Concern

    included do
      include Gitlab::EncryptedAttribute

      belongs_to :integration, inverse_of: self.table_name.to_sym, foreign_key: :integration_id, optional: true
      # rubocop:disable Rails/InverseOf -- the parent models have no matching has_one
      # reflection for the per-data-field table, so any inverse_of: here would point at
      # a non-existent association and raise InverseOfAssociationNotFoundError when
      # Reflection#check_validity! runs (e.g. during let_it_be(freeze: true) deep_freeze).
      belongs_to :project, foreign_key: :project_id, optional: true
      belongs_to :group, foreign_key: :group_id, optional: true
      belongs_to :organization, foreign_key: :organization_id, optional: true,
        class_name: 'Organizations::Organization'
      # rubocop:enable Rails/InverseOf

      before_validation :set_sharding_key

      validates :integration, presence: true
      validates :project_id, :group_id, absence: true, if: -> { organization_level? }
      validates :project_id, :organization_id, absence: true, if: -> { group_level? }
      validates :group_id, :organization_id, absence: true, if: -> { project_level? }

      validates_with ExactlyOnePresentValidator, fields: [:project_id, :group_id, :organization_id]
    end

    class_methods do
      def encryption_options
        {
          key: :db_key_base_32,
          encode: true,
          mode: :per_attribute_iv,
          algorithm: 'aes-256-gcm'
        }
      end
    end

    def activated?
      !!integration&.activated?
    end

    def to_database_hash
      as_json(
        only: self.class.column_names
      ).except(
        'id',
        'service_id',
        'integration_id',
        'created_at',
        'updated_at',
        'group_id',
        'project_id',
        'organization_id'
      )
    end

    private

    def set_sharding_key
      return if project_id || group_id || organization_id || integration.nil?

      self.project_id = integration.project_id if integration.project_id
      self.group_id = integration.group_id if integration.group_id
      self.organization_id = integration.organization_id if integration.organization_id
    end

    def project_level?
      project_id.present?
    end

    def group_level?
      group_id.present?
    end

    def organization_level?
      organization_id.present?
    end
  end
end
