class Rage::OpenAPI::Parsers::Ext::Alba::Visitor

def build_schema

def build_schema
  result = { "type" => "object" }
  result["properties"] = @schema if @schema.any?
  if @root_key_proc
    dynamic_root_key, dynamic_root_key_for_collection = @root_key_proc.call(@self_name)
    @root_key = dynamic_root_key
    @root_key_for_collection = dynamic_root_key_for_collection
  end
  if @is_collection
    result = if @collection_key && @root_key_for_collection
      { "type" => "object", "properties" => { @root_key_for_collection => { "type" => "object", "additionalProperties" => result }, **@meta } }
    elsif @collection_key
      { "type" => "object", "additionalProperties" => result }
    elsif @root_key_for_collection
      { "type" => "object", "properties" => { @root_key_for_collection => { "type" => "array", "items" => result }, **@meta } }
    else
      { "type" => "array", "items" => result }
    end
  elsif @root_key
    result = { "type" => "object", "properties" => { @root_key => result, **@meta } }
  end
  result = deep_transform_keys(result) if @key_transformer
  result
end

def deep_transform_keys(schema)

def deep_transform_keys(schema)
  schema.each_with_object({}) do |(key, value), memo|
    transformed_key = %w(type properties items additionalProperties).include?(key) ? key : @key_transformer.call(key)
    memo[transformed_key] = value.is_a?(Hash) ? deep_transform_keys(value) : value
  end
end

def get_key_transformer(transformer_id)

def get_key_transformer(transformer_id)
  return nil unless ::Alba.inflector
  case transformer_id
  when "camel"
    ->(key) { ::Alba.inflector.camelize(key) }
  when "lower_camel"
    ->(key) { ::Alba.inflector.camelize_lower(key) }
  when "dash"
    ->(key) { ::Alba.inflector.dasherize(key) }
  when "snake"
    ->(key) { ::Alba.inflector.underscore(key) }
  end
end

def get_type_definition(type_id)

def get_type_definition(type_id)
  Rage::OpenAPI.__type_to_spec(type_id.delete_prefix(":"), default: true)
end

def hash_to_openapi_schema(hash)

def hash_to_openapi_schema(hash)
  return { "type" => "object" } unless hash
  schema = hash.each_with_object({}) do |(key, value), memo|
    memo[key.to_s] = if value.is_a?(Hash)
      hash_to_openapi_schema(value)
    elsif value.is_a?(Array)
      { "type" => "array", "items" => { "type" => "string" } }
    else
      { "type" => "string" }
    end
  end
  { "type" => "object", "properties" => schema }
end

def initialize(parser, is_collection)

def initialize(parser, is_collection)
  @parser = parser
  @is_collection = is_collection
  @schema = {}
  @segment = @schema
  @context = nil
  @prev_contexts = []
  @self_name = nil
  @root_key = nil
  @root_key_for_collection = nil
  @root_key_proc = nil
  @key_transformer = nil
  @collection_key = false
  @meta = {}
end

def visit_assoc_node(node)

def visit_assoc_node(node)
  value = case node.value
  when Prism::StringNode
    node.value.content
  when Prism::ArrayNode
    context = with_context { visit(node.value) }
    context.symbols[0] || context.consts[0]
  else
    node.value.slice
  end
  @context.keywords[node.key.value] = value
end

def visit_call_node(node)

def visit_call_node(node)
  case node.name
  when :root_key
    @root_key_proc = nil
    context = with_context { visit(node.arguments) }
    @root_key, @root_key_for_collection = context.symbols
  when :attributes, :attribute
    context = with_context { visit(node.arguments) }
    context.symbols.each { |symbol| @segment[symbol] = { "type" => "string" } }
    context.keywords.except("if").each { |key, type| @segment[key] = get_type_definition(type) }
  when :nested, :nested_attribute
    context = with_context { visit(node.arguments) }
    with_inner_segment(context.symbols[0]) { visit(node.block) }
  when :meta
    context = with_context do
      visit(node.arguments)
      visit(node.block)
    end
    key = context.symbols[0] || "meta"
    unless context.nil
      @meta = { key => hash_to_openapi_schema(context.hashes[0]) }
    end
  when :many, :has_many, :one, :has_one, :association
    is_array = node.name == :many || node.name == :has_many
    context = with_context { visit(node.arguments) }
    association = context.symbols[0]
    key = context.keywords["key"] || association
    if node.block
      with_inner_segment(key, is_array:) { visit(node.block) }
    else
      resource = context.keywords["resource"] || (::Alba.inflector && "#{::Alba.inflector.classify(association.to_s)}Resource")
      is_valid_resource = @parser.namespace.const_get(resource) rescue false
      @segment[key] = if is_array
        @parser.__parse_nested(is_valid_resource ? "[#{resource}]" : "[Rage]") # TODO
      else
        @parser.__parse_nested(is_valid_resource ? resource : "Rage")
      end
    end
  when :transform_keys
    context = with_context { visit(node.arguments) }
    @key_transformer = get_key_transformer(context.symbols[0])
  when :collection_key
    @collection_key = true
  when :root_key!
    if (inflector = ::Alba.inflector)
      @root_key, @root_key_for_collection = nil
      @root_key_proc = ->(resource_name) do
        suffix = resource_name.end_with?("Resource") ? "Resource" : "Serializer"
        name = inflector.demodulize(resource_name).delete_suffix(suffix)
        inflector.underscore(name).yield_self { |key| [key, inflector.pluralize(key)] }
      end
    end
  end
end

def visit_class_node(node)

def visit_class_node(node)
  @self_name ||= node.name.to_s
  if node.name =~ /Resource$|Serializer$/ && node.superclass
    visitor = @parser.__parse(node.superclass.name)
    @root_key, @root_key_for_collection, @root_key_proc = visitor.root_key, visitor.root_key_for_collection, visitor.root_key_proc
    @key_transformer, @collection_key, @meta = visitor.key_transformer, visitor.collection_key, visitor.meta
    @schema.merge!(visitor.schema)
  end
  super
end

def visit_constant_read_node(node)

def visit_constant_read_node(node)
  return unless @context
  @context.consts << node.name.to_s
end

def visit_hash_node(node)

def visit_hash_node(node)
  parsed_hash = YAML.safe_load(node.slice) rescue nil
  @context.hashes << parsed_hash if parsed_hash
end

def visit_nil_node(node)

def visit_nil_node(node)
  @context.nil = true
end

def visit_symbol_node(node)

def visit_symbol_node(node)
  @context.symbols << node.value
end

def with_context

def with_context
  @prev_contexts << @context if @context
  @context = VisitorContext.new
  yield
  current_context = @context
  @context = @prev_contexts.pop
  current_context
end

def with_inner_segment(key, is_array: false)

def with_inner_segment(key, is_array: false)
  prev_segment = @segment
  properties = {}
  if is_array
    @segment[key] = { "type" => "array", "items" => { "type" => "object", "properties" => properties } }
  else
    @segment[key] = { "type" => "object", "properties" => properties }
  end
  @segment = properties
  yield
  @segment = prev_segment
end