module JSONAPI::Support::NestedFilters

def apply_column_operator_filter(scope, target_model, filter_name, filter_value)

def apply_column_operator_filter(scope, target_model, filter_name, filter_value)
  column_filter = parse_column_filter(filter_name)
  return nil unless column_filter
  column = target_model.column_for_attribute(column_filter[:column])
  return nil unless column
  value = normalize_filter_value_for_model(target_model, column, filter_value)
  return nil unless value
  condition = build_condition(target_model, column, value, column_filter[:operator])
  condition ? apply_condition(scope, condition) : nil
end

def apply_direct_column_filter(scope, target_model, filter_name, filter_value)

def apply_direct_column_filter(scope, target_model, filter_name, filter_value)
  return nil unless target_model.column_names.include?(filter_name)
  scope.where(target_model.table_name => { filter_name => filter_value })
end

def apply_filter_for_path(scope, filter_name, filter_value)

def apply_filter_for_path(scope, filter_name, filter_value)
  parts = filter_name.split(".")
  return scope if parts.length < 2
  relationship_chain = parts[0..-2]
  leaf_filter = parts.last
  result = traverse_relationship_chain(scope, relationship_chain, leaf_filter, filter_value)
  return result[:scope] if result[:early_return]
  apply_joined_filter(scope, result, relationship_chain:, leaf_filter:, filter_value:)
end

def apply_filter_on_model(scope, target_model, target_resource, filter_name, filter_value)

def apply_filter_on_model(scope, target_model, target_resource, filter_name, filter_value)
  return scope if empty_filter_value?(filter_value)
  apply_column_operator_filter(scope, target_model, filter_name, filter_value) ||
    apply_direct_column_filter(scope, target_model, filter_name, filter_value) ||
    apply_scope_method_filter(scope, target_model, target_resource, filter_name, filter_value) ||
    scope
end

def apply_joined_filter(scope, result, relationship_chain:, leaf_filter:, filter_value:)

def apply_joined_filter(scope, result, relationship_chain:, leaf_filter:, filter_value:)
  join_hash = build_join_hash_for_chain(relationship_chain)
  scope = scope.joins(join_hash) if join_hash.present?
  apply_filter_on_model(scope, result[:model], result[:definition], leaf_filter, filter_value)
end

def apply_nested_relationship_filters(scope)

def apply_nested_relationship_filters(scope)
  return scope if filter_params.empty?
  nested_filters = filter_params.select { |k, _v| k.to_s.include?(".") }
  return scope if nested_filters.empty?
  nested_filters.reduce(scope) do |current_scope, (filter_name, filter_value)|
    apply_filter_for_path(current_scope, filter_name.to_s, filter_value)
  end
end

def apply_scope_method_filter(scope, target_model, target_resource, filter_name, filter_value)

def apply_scope_method_filter(scope, target_model, target_resource, filter_name, filter_value)
  if target_model.respond_to?(filter_name.to_sym)
    return try_scope_method(scope, target_model, filter_name,
                            filter_value,)
  end
  return nil unless target_resource
  return nil unless target_resource.permitted_filters.map(&:to_s).include?(filter_name)
  return nil unless target_model.respond_to?(filter_name.to_sym)
  try_scope_method(scope, target_model, filter_name, filter_value)
end

def build_join_hash_for_chain(chain)

def build_join_hash_for_chain(chain)
  return nil if chain.empty?
  chain.reverse.reduce(nil) do |acc, name|
    if acc.nil?
      name.to_sym
    else
      { name.to_sym => acc }
    end
  end
end

def process_chain_step(scope, current_model, relationship_name, leaf_filter, filter_value)

def process_chain_step(scope, current_model, relationship_name, leaf_filter, filter_value)
  association = current_model.reflect_on_association(relationship_name.to_sym)
  return { scope:, early_return: true } unless association
  if association.polymorphic?
    attributes = { leaf_filter => filter_value }
    return { scope: apply_polymorphic_nested_filters(scope, association, relationship_name, attributes),
             early_return: true, }
  end
  next_definition = JSONAPI::Resource.resource_for_model(association.klass)
  return { scope:, early_return: true } unless next_definition
  { model: association.klass, definition: next_definition, early_return: false }
end

def traverse_relationship_chain(scope, relationship_chain, leaf_filter, filter_value)

def traverse_relationship_chain(scope, relationship_chain, leaf_filter, filter_value)
  current_model = model_class
  current_definition = definition
  relationship_chain.each do |relationship_name|
    result = process_chain_step(scope, current_model, relationship_name, leaf_filter, filter_value)
    return result if result[:early_return]
    current_model = result[:model]
    current_definition = result[:definition]
  end
  { model: current_model, definition: current_definition, early_return: false }
end

def try_scope_method(scope, target_model, filter_name, filter_value)

def try_scope_method(scope, target_model, filter_name, filter_value)
  scope.merge(target_model.public_send(filter_name.to_sym, filter_value))
rescue ArgumentError, NoMethodError
  nil
end