# frozen_string_literal: true

require 'unscoped_associations/version'

module UnscopedAssociations
  def self.included(base)
    class << base
      prepend ClassMethods
    end
  end

  module ClassMethods
    def belongs_to(name, scope = nil, **options)
      add_unscoped_association(:belongs_to, name) if options.delete(:unscoped)
      super(name, scope, **options)
    end

    def has_many(name, scope = nil, **options, &extension)
      add_unscoped_association(:has_many, name) if options.delete(:unscoped)
      super(name, scope, **options, &extension)
    end

    def has_one(name, scope = nil, **options)
      add_unscoped_association(:has_one, name) if options.delete(:unscoped)
      super(name, scope, **options)
    end

    private

    def add_unscoped_association(association_type, association_name)
      cache_name = "@_cache_#{association_name}"
      define_method(association_name) do |*args|
        if (args[0]).nil? # args[0] is force_reload
          instance_variable_get(cache_name) || instance_variable_set(
            cache_name,
            association(association_name).klass.unscoped do
              association_type == :has_many ? super().reload : public_send("reload_#{association_name}")
            end
          )
        else
          instance_variable_set(cache_name, association(association_name).klass.unscoped { super(true) })
        end
      end
    end
  end
end

ActiveRecord::Base.include UnscopedAssociations
