# frozen_string_literal: true

require 'global_registry'
require 'global_registry_bindings/workers/delete_entity_worker'

module GlobalRegistry #:nodoc:
  module Bindings #:nodoc:
    module Entity #:nodoc:
      module PushRelationshipMethods
        extend ActiveSupport::Concern

        def push_relationship_to_global_registry
          ensure_related_entities_have_global_registry_ids!
          push_global_registry_relationship_type
          create_relationship_in_global_registry
        end

        def create_relationship_in_global_registry
          entity = put_relationship_to_global_registry
          global_registry_relationship(type).id_value = global_registry_relationship_entity_id_from_entity entity
          model.update_column( # rubocop:disable Rails/SkipsModelValidations
            global_registry_relationship(type).id_column,
            global_registry_relationship(type).id_value
          )
        end

        def global_registry_relationship_entity_id_from_entity(entity)
          relationships = Array.wrap entity.dig(
            'entity',
            global_registry_relationship(type).primary_type.to_s,
            "#{global_registry_relationship(type).related_relationship_name}:relationship"
          )
          relationships.detect do |rel|
            cid = rel['client_integration_id']
            cid = cid['value'] if cid.is_a?(Hash)
            cid == global_registry_relationship(type).client_integration_id.to_s
          end&.dig('relationship_entity_id')
        end

        def ensure_related_entities_have_global_registry_ids! # rubocop:disable Metrics/AbcSize
          if global_registry_relationship(type).primary_id_value.present? &&
             global_registry_relationship(type).related_id_value.present?
            return
          end
          # Enqueue push_entity worker for related entities missing global_registry_id and retry relationship push
          names = []
          [global_registry_relationship(type).primary, global_registry_relationship(type).related].each do |model|
            next if model.global_registry_entity.id_value?
            names << "#{model.class.name}(#{model.id})"
            model.push_entity_to_global_registry_async
          end
          raise GlobalRegistry::Bindings::RelatedEntityMissingGlobalRegistryId,
                "#{model.class.name}(#{model.id}) has related entities [#{names.join ', '}] missing " \
                'global_registry_id; will retry.'
        end

        def relationship_entity
          { entity:  { global_registry_relationship(type).primary_type => {
            "#{global_registry_relationship(type).related_relationship_name}:relationship" =>
              model.relationship_attributes_to_push(type)
                   .merge(global_registry_relationship(type).related_type =>
                         global_registry_relationship(type).related_id_value)
          }, client_integration_id: global_registry_relationship(type).primary.id } }
        end

        def put_relationship_to_global_registry
          GlobalRegistry::Entity.put(
            global_registry_relationship(type).primary_id_value,
            relationship_entity,
            params: {
              full_response: true,
              fields: "#{global_registry_relationship(type).related_relationship_name}:relationship"
            }
          )
        rescue RestClient::BadRequest => e
          response = JSON.parse(e.response.body)
          raise unless response['error'] =~ /^Validation failed:.*already exists$/i
          # Delete relationship entity and retry on 400 Bad Request (client_integration_id already exists)
          delete_relationship_from_global_registry_and_retry
        end

        def delete_relationship_from_global_registry_and_retry
          GlobalRegistry::Bindings::Workers::DeleteEntityWorker.new.perform(global_registry_relationship(type).id_value)
          model.update_column( # rubocop:disable Rails/SkipsModelValidations
            global_registry_relationship(type).id_column, nil
          )
          raise GlobalRegistry::Bindings::RelatedEntityExistsWithCID,
                "#{model.class.name}(#{model.id}) #{global_registry_relationship(type).related_relationship_name}" \
                ':relationship already exists with client_integration_id(' \
                "#{global_registry_relationship(type).client_integration_id}). Will delete and retry."
        end
      end
    end
  end
end