module Prism
  class Node
    attr_reader source: Source
    attr_reader node_id: Integer
    attr_reader location: Location
    attr_reader flags: Integer

    def newline?: () -> bool
    def static_literal?: () -> bool

    def accept: (_Visitor) -> void
    def child_nodes: () -> Array[Prism::node?]
    def comment_targets: () -> Array[Prism::node | Location]
    def compact_child_nodes: () -> Array[Prism::node]
    def self.fields: () -> Array[Prism::Reflection::Field]
    def type: () -> Symbol
    def self.type: () -> Symbol

    def source_lines: () -> Array[String]
    alias script_lines source_lines
    def slice: () -> String
    def slice_lines: () -> String
    def pretty_print: (untyped q) -> untyped
    def to_dot: () -> String
    def tunnel: (Integer line, Integer column) -> Array[Prism::node]
    def breadth_first_search: () { (Prism::node) -> bool } -> Prism::node?
    def newline!: (Array[untyped]) -> void

    def save: (_Repository repository) -> void
    def save_location: (_Repository repository) -> void

    def leading_comments: () -> Array[comment]
    def trailing_comments: () -> Array[comment]
    def comments: () -> Array[comment]

    def start_offset: () -> Integer
    def start_character_offset: () -> Integer
    def start_code_units_offset: (Encoding encoding) -> Integer
    def cached_start_code_units_offset: (_CodeUnitsCache cache) -> Integer

    def end_offset: () -> Integer
    def end_character_offset: () -> Integer
    def end_code_units_offset: (Encoding encoding) -> Integer
    def cached_end_code_units_offset: (_CodeUnitsCache cache) -> Integer

    def start_line: () -> Integer
    def end_line: () -> Integer

    def start_column: () -> Integer
    def start_character_column: () -> Integer
    def start_code_units_column: (Encoding encoding) -> Integer
    def cached_start_code_units_column: (_CodeUnitsCache cache) -> Integer

    def end_column: () -> Integer
    def end_character_column: () -> Integer
    def end_code_units_column: (Encoding encoding) -> Integer
    def cached_end_code_units_column: (_CodeUnitsCache cache) -> Integer
  end

  # Methods implemented by every subclass of Node
  interface _Node
    def deconstruct: () -> Array[Prism::node?]
    def inspect: () -> String
  end

  type node = Node & _Node

  interface _Repository
    def enter: (Integer node_id, Symbol field_name) -> void
  end

  <%- nodes.each do |node| -%>

  <%- node.each_comment_line do |line| -%>
  #<%= line %>
  <%- end -%>
  class <%= node.name -%> < Node
    include _Node
    <%- if (node_flags = node.flags) -%>

    <%- node_flags.values.each do |value| -%>
    def <%= value.name.downcase %>?: () -> bool
    <%- end -%>
    <%- end -%>

    <%- node.fields.each do |field| -%>
    attr_reader <%= field.name %>: <%= field.rbs_class %>
    <%- end -%>
    <%- if (locations = node.fields.select { |field| field.is_a?(Prism::Template::LocationField) || field.is_a?(Prism::Template::OptionalLocationField) }) -%>

    <%- locations.each do |field| -%>
    def save_<%= field.name %>: (_Repository repository) -> void
    <%- end -%>
    <%- end -%>

    def initialize: (<%= ["Source source", "Integer node_id", "Location location", "Integer flags", *node.fields.map { |field| "#{field.rbs_class} #{field.name}" }].join(", ") %>) -> void
    def copy: (<%= (["?node_id: Integer", "?location: Location", "?flags: Integer"] + node.fields.map { |field| "?#{field.name}: #{field.rbs_class}" }).join(", ") %>) -> <%= node.name %>
    def deconstruct_keys: (Array[Symbol] keys) -> { <%= (["node_id: Integer", "location: Location"] + node.fields.map { |field| "#{field.name}: #{field.rbs_class}" }).join(", ") %> }
    <%- node.fields.each do |field| -%>
    <%- case field -%>
    <%- when Prism::Template::LocationField -%>
    <%- raise unless field.name.end_with?("_loc") -%>
    <%- next if node.fields.any? { |other| other.name == field.name.delete_suffix("_loc") } -%>
    def <%= field.name.delete_suffix("_loc") %>: () -> String
    <%- when Prism::Template::OptionalLocationField -%>
    <%- raise unless field.name.end_with?("_loc") -%>
    <%- next if node.fields.any? { |other| other.name == field.name.delete_suffix("_loc") } -%>
    def <%= field.name.delete_suffix("_loc") %>: () -> String?
    <%- end -%>
    <%- end -%>
    def type: () -> :<%= node.human %>
            | ...
    def self.type: () -> :<%= node.human %>
  end
  <%- end -%>
  <%- flags.each do |flag| -%>

  # <%= flag.comment %>
  module <%= flag.name %>
    <%- flag.values.each do |value| -%>
    # <%= value.comment %>
    <%= value.name %>: Integer
    <%- end -%>
  end
  <%- end -%>

  # The flags that are common to all nodes.
  module NodeFlags
    NEWLINE: Integer
    STATIC_LITERAL: Integer
  end
end
