module Ariadne::Static::GenerateStructure

def call

def call
  components = Ariadne::BaseComponent.descendants.sort_by(&:name) - [Ariadne::BaseComponent]
  component_docs = components.each_with_object({}) do |component, memo|
    docs = registry.find(component)
    preview_data = previews.find do |preview|
      preview["component"] == docs.metadata[:title]
    end
    arg_data = args.find do |component_args|
      component_args["component"] == docs.metadata[:title]
    end
    slot_docs = docs.slot_methods.map do |slot_method|
      param_tags = slot_method.tags(:param)
      description = if slot_method.base_docstring.to_s.present?
        render_erb_ignoring_markdown_code_fences(slot_method.base_docstring).force_encoding("UTF-8")
      else
        ""
      end
      {
        "name" => slot_method.name,
        "description" => description,
        "parameters" => serialize_params(param_tags, component),
      }
    end
    mtds = docs.non_slot_methods.select do |mtd|
      next false if mtd.base_docstring.to_s.blank?
      next false if SKIP_METHODS.include?(mtd.name)
      method_location, = mtd.files.first
      class_location, = docs.docs.files.first
      method_location == class_location
    end
    method_docs = mtds.map do |mtd|
      param_tags = mtd.tags(:param)
      {
        "name" => mtd.name,
        "description" => render_erb_ignoring_markdown_code_fences(mtd.base_docstring),
        "parameters" => serialize_params(param_tags, component),
      }
    end
    description =
      if component == Ariadne::BaseComponent
        docs.base_docstring
      else
        render_erb_ignoring_markdown_code_fences(docs.base_docstring)
      end
    accessibility_docs =
      if (accessibility_tag_text = docs.tags(:accessibility)&.first&.text)
        render_erb_ignoring_markdown_code_fences(accessibility_tag_text)
      end
    behavior_docs =
      if (behavior_tag_text = docs.tags(:behaviors)&.first&.text)
        render_erb_ignoring_markdown_code_fences(behavior_tag_text)
      end
    memo[component.name] = {
      "fully_qualified_name" => component.name,
      "description" => description,
      "accessibility_docs" => accessibility_docs,
      "behavior_docs" => behavior_docs,
      "is_form_component" => docs.manifest_entry.form_component?,
      "requires_js" => docs.manifest_entry.requires_js?,
      **arg_data,
      "slots" => slot_docs,
      "methods" => method_docs,
      "previews" => (preview_data || {}).fetch("examples", []),
      "subcomponents" => [],
    }
  end
  Ariadne::BaseComponent.descendants.sort_by(&:name).each do |component|
    fq_class = component.name.to_s.split("::")
    fq_class.shift # remove Ariadne::
    type = fq_class.shift # remove {UI,Form,*}::
    parent, *child = *fq_class
    next if child.empty? || child.length < 2
    parent_class = "Ariadne::#{type}".constantize
    parent_class = parent_class.const_get(parent)
    parent_docs = component_docs["#{parent_class}::Component"]
    next unless parent_docs
    if (child_docs = component_docs.delete(component.name))
      parent_docs["subcomponents"] << child_docs
    end
  end
  toc_categories = {
    "UI" => [],
    "Form" => [],
    "Layout" => [],
    "Behaviors" => [],
  }
  component_docs.values.each do |component|
    type, name = component["short_name"].split("::", 2)
    next unless toc_categories[type] # not a required category
    # removes children from toc, like `Ariadne::UI::Accordion::Item`,
    # by not adding the component to the TOC if it has more than one level of nesting
    next if name.include?("::")
    slug = component["short_name"].gsub("::", "/").downcase
    toc_categories[type] << {
      "name" => name,
      "slug" => slug,
    }
    File.open(File.join(DEFAULT_STATIC_PATH, FILE_NAMES[:toc]), "w") do |f|
      f.write(JSON.pretty_generate(toc_categories))
      f.write($INPUT_RECORD_SEPARATOR)
    end
  end
  component_docs.values
end