class EagerEye::Detectors::SerializerNesting

def self.detector_name

def self.detector_name
  :serializer_nesting
end

def alba_resource?(include_node)

def alba_resource?(include_node)
  arg = include_node.children[2]
  arg && const_to_string(arg)&.include?("Alba")
end

def attribute_block?(node)

def attribute_block?(node)
  node.type == :block && node.children[0]&.type == :send &&
    ATTRIBUTE_METHODS.include?(node.children[0].children[1])
end

def detect(ast, file_path)

def detect(ast, file_path)
  return [] unless ast
  issues = []
  traverse_ast(ast) do |node|
    next unless node.type == :class && serializer_class?(node)
    find_nested_associations(node, file_path, issues)
  end
  issues
end

def find_association_in_block(block_body, file_path, issues)

def find_association_in_block(block_body, file_path, issues)
  storage_lines = collect_active_storage_lines(block_body)
  traverse_ast(block_body) do |node|
    next unless node.type == :send
    next if storage_lines.include?(node.loc.line)
    receiver = node.children[0]
    method_name = node.children[1]
    next unless object_reference?(receiver) && likely_association?(method_name)
    issues << create_issue(
      file_path: file_path,
      line_number: node.loc.line,
      message: "Nested association `#{receiver_name(receiver)}.#{method_name}` in serializer attribute",
      suggestion: "Eager load :#{method_name} in controller or use association serializer"
    )
  end
end

def find_nested_associations(class_node, file_path, issues)

def find_nested_associations(class_node, file_path, issues)
  body = class_node.children[2]
  return unless body
  traverse_ast(body) do |node|
    next unless attribute_block?(node) && node.children[2]
    find_association_in_block(node.children[2], file_path, issues)
  end
end

def includes_serializer_module?(class_node)

def includes_serializer_module?(class_node)
  body = class_node.children[2]
  return false unless body
  traverse_ast(body) do |node|
    return true if node.type == :send && node.children[1] == :include && alba_resource?(node)
  end
  false
end

def inherits_from_serializer?(class_node)

def inherits_from_serializer?(class_node)
  parent_node = class_node.children[1]
  return false unless parent_node
  parent_name = const_to_string(parent_node)
  SERIALIZER_PATTERNS.any? { |p| parent_name&.include?(p.split("::").last) }
end

def object_reference?(node)

def object_reference?(node)
  return false unless node
  case node.type
  when :send then node.children[0].nil? && OBJECT_REFS.include?(node.children[1])
  when :lvar then true
  else false
  end
end

def receiver_name(node)

def receiver_name(node)
  case node.type
  when :send then node.children[1].to_s
  when :lvar then node.children[0].to_s
  else "object"
  end
end

def serializer_class?(node)

def serializer_class?(node)
  class_name = extract_class_name(node)
  return false unless class_name
  class_name.end_with?("Serializer", "Blueprint", "Resource") ||
    inherits_from_serializer?(node) || includes_serializer_module?(node)
end