module Prism
module Merge
VERSION: String

module Version
  VERSION: String
end

class Error < StandardError
end

class ParseError < Error
  attr_reader content: String
  attr_reader parse_result: untyped

  def initialize: (String message, content: String, parse_result: untyped) -> void
end

class TemplateParseError < ParseError
end

class DestinationParseError < ParseError
end

# Wrapper to represent freeze blocks as first-class nodes.
# Freeze blocks can be placed inside:
# - ClassNode, ModuleNode, SingletonClassNode (class/module definitions)
# - DefNode (method definitions)
# - LambdaNode (lambda/proc definitions)
# - CallNode with blocks (e.g., RSpec describe/context blocks)
class FreezeNode
  class InvalidStructureError < StandardError
    attr_reader start_line: Integer?
    attr_reader end_line: Integer?
    attr_reader unclosed_nodes: Array[untyped]

    def initialize: (String message, ?start_line: Integer?, ?end_line: Integer?, ?unclosed_nodes: Array[untyped]) -> void
  end

  class Location
    attr_reader start_line: Integer
    attr_reader end_line: Integer

    def initialize: (Integer start_line, Integer end_line) -> void
  end

  attr_reader start_line: Integer
  attr_reader end_line: Integer
  attr_reader content: String
  attr_reader nodes: Array[untyped]
  attr_reader start_marker: String?
  attr_reader end_marker: String?

  def initialize: (
    start_line: Integer,
    end_line: Integer,
    analysis: FileAnalysis,
    ?nodes: Array[untyped],
    ?overlapping_nodes: Array[untyped]?,
    ?start_marker: String?,
    ?end_marker: String?
  ) -> void

  def location: () -> Location

  def signature: () -> Array[Symbol | Integer]

  def line_range: () -> Range[Integer]

  def contains_line?: (Integer line_num) -> bool

  def overlaps?: (untyped other) -> bool

  def to_s: () -> String

  def inspect: () -> String

  private

  def validate_structure!: () -> void
end

class FileAnalysis
  FREEZE_START: Regexp
  FREEZE_END: Regexp
  FREEZE_BLOCK: Regexp

  type freeze_block = {
    range: Range[Integer],
    line_range: Range[Integer],
    text: String,
    start_marker: String?
  }

  type node_info = {
    node: untyped,
    index: Integer,
    leading_comments: Array[untyped],
    inline_comments: Array[untyped],
    signature: Array[untyped]?,
    line_range: Range[Integer]
  }

  attr_reader content: String
  attr_reader parse_result: untyped
  attr_reader lines: Array[String]
  attr_reader statements: Array[untyped]
  attr_reader freeze_blocks: Array[freeze_block]

  # Custom signature generator return types:
  # - Array[untyped]: Used as the node's signature for matching
  # - nil: Node gets no signature (won't be matched by signature)
  # - Prism::Node or FreezeNode: Falls through to default signature computation
  def initialize: (String content, ?signature_generator: (^(untyped) -> (Array[untyped] | untyped | nil))?, ?freeze_token: String?) -> void

  def valid?: () -> bool

  def extract_statements: () -> Array[untyped]

  def extract_freeze_blocks: () -> Array[freeze_block]

  def line_to_node_map: () -> Hash[Integer, Array[untyped]]

  def node_to_line_map: () -> Hash[untyped, Range[Integer]]

  def nodes_with_comments: () -> Array[node_info]

  def comment_map: () -> Hash[Integer, Array[untyped]]

  def signature_at: (Integer index) -> Array[untyped]?

  def generate_signature: (untyped node) -> Array[untyped]?

  def in_freeze_block?: (Integer line_num) -> bool

  def freeze_block_at: (Integer line_num) -> freeze_block?

  def normalized_line: (Integer line_num) -> String?

  def line_at: (Integer line_num) -> String?

  private

  def build_line_to_node_map: () -> Hash[Integer, Array[untyped]]

  def build_node_to_line_map: () -> Hash[untyped, Range[Integer]]

  def extract_nodes_with_comments: () -> Array[node_info]

  def find_leading_comments: (untyped current_stmt, untyped? prev_stmt, untyped body_node) -> Array[untyped]

  def inline_comments_for_node: (untyped stmt) -> Array[untyped]

  def build_comment_map: () -> Hash[Integer, Array[untyped]]

  def default_signature: (untyped node) -> Array[untyped]

  # Compute structural signature for node matching
  # Returns signature arrays like:
  #   [:def, Symbol, Array[Symbol]]           - method definitions
  #   [:class, String]                        - class definitions
  #   [:module, String]                       - module definitions
  #   [:singleton_class, String]              - singleton class definitions
  #   [:const, Symbol | String]               - constant assignments
  #   [:local_var, Symbol]                    - local variable assignments
  #   [:ivar, Symbol]                         - instance variable assignments
  #   [:cvar, Symbol]                         - class variable assignments
  #   [:gvar, Symbol]                         - global variable assignments
  #   [:multi_write, Array[Symbol | String]]  - multiple assignment
  #   [:if, String] | [:unless, String]       - conditionals
  #   [:case, String] | [:case_match, String] - case statements
  #   [:while, String] | [:until, String]     - while/until loops
  #   [:for, String, String]                  - for loops
  #   [:begin, String]                        - begin blocks
  #   [:call, Symbol, String?]                - method calls
  #   [:call_with_block, Symbol, String?]     - method calls with blocks
  #   [:super, Symbol]                        - super calls
  #   [:forwarding_super, Symbol]             - forwarding super calls
  #   [:lambda, String]                       - lambda expressions
  #   [:pre_execution, Integer]               - BEGIN blocks
  #   [:post_execution, Integer]              - END blocks
  #   [:parens, String]                       - parenthesized expressions
  #   [:embedded, String]                     - embedded statements
  #   [:other, String, Integer]               - fallback for unknown nodes
  def compute_node_signature: (untyped node) -> Array[untyped]

  # Extract first argument value from a CallNode
  def extract_first_argument_value: (untyped node) -> (String | Symbol | nil)
end

class FileAligner
  class Anchor < Struct[untyped]
    attr_accessor template_start: Integer
    attr_accessor template_end: Integer
    attr_accessor dest_start: Integer
    attr_accessor dest_end: Integer
    attr_accessor match_type: Symbol
    attr_accessor score: Integer

    def template_range: () -> Range[Integer]

    def dest_range: () -> Range[Integer]

    def length: () -> Integer
  end

  class Boundary < Struct[untyped]
    attr_accessor template_range: Range[Integer]?
    attr_accessor dest_range: Range[Integer]?
    attr_accessor prev_anchor: Anchor?
    attr_accessor next_anchor: Anchor?

    def template_lines: () -> Array[Integer]

    def dest_lines: () -> Array[Integer]
  end

  attr_reader template_analysis: FileAnalysis
  attr_reader dest_analysis: FileAnalysis
  attr_reader anchors: Array[Anchor]
  attr_reader boundaries: Array[Boundary]

  def initialize: (FileAnalysis template_analysis, FileAnalysis dest_analysis) -> void

  def align: () -> Array[Boundary]

  private

  def find_anchors: () -> void

  def build_line_map: (FileAnalysis analysis) -> Hash[Integer, String]

  def find_exact_line_matches: (Hash[Integer, String] template_map, Hash[Integer, String] dest_map) -> Array[Hash[Symbol, untyped]]

  def merge_consecutive_matches: (Array[Hash[Symbol, untyped]] matches) -> Array[Anchor]

  def add_node_signature_anchors: () -> void

  def add_freeze_block_anchors: () -> void

  def compute_boundaries: () -> void

  def ranges_overlap?: (Range[Integer]? range1, Range[Integer]? range2) -> bool
end

class MergeResult
  DECISION_KEPT_TEMPLATE: Symbol
  DECISION_KEPT_DEST: Symbol
  DECISION_APPENDED: Symbol
  DECISION_REPLACED: Symbol
  DECISION_FREEZE_BLOCK: Symbol

  type line_metadata = {
    decision: Symbol,
    template_line: Integer?,
    dest_line: Integer?,
    comment: String?,
    result_line: Integer
  }

  attr_reader lines: Array[String]
  attr_reader line_metadata: Array[line_metadata]

  def initialize: () -> void

  def add_line: (String content, decision: Symbol, ?template_line: Integer?, ?dest_line: Integer?, ?comment: String?) -> void

  def add_lines_from: (Array[String] source_lines, decision: Symbol, source: Symbol, start_line: Integer, ?comment: String?) -> void

  def add_node: (FileAnalysis::node_info node_info, decision: Symbol, source: Symbol, ?source_analysis: FileAnalysis?) -> void

  def to_s: () -> String

  def statistics: () -> Hash[Symbol, Integer]

  def lines_by_decision: (Symbol decision) -> Array[line_metadata]

  def debug_output: () -> String
end

class ConflictResolver
  type boundary_content = {
    lines: Array[String],
    nodes: Array[FileAnalysis::node_info],
    has_freeze_block: bool,
    line_range: Range[Integer]?
  }

  attr_reader template_analysis: FileAnalysis
  attr_reader dest_analysis: FileAnalysis
  attr_reader add_template_only_nodes: bool

  def initialize: (FileAnalysis template_analysis, FileAnalysis dest_analysis, ?preference: (Symbol | Hash[Symbol, Symbol]), ?add_template_only_nodes: bool) -> void

  def resolve: (FileAligner::Boundary boundary, MergeResult result) -> void

  private

  def extract_boundary_content: (FileAnalysis analysis, Range[Integer]? line_range) -> boundary_content

  def ranges_overlap?: (Range[Integer] range1, Range[Integer] range2) -> bool

  def add_content_to_result: (boundary_content content, MergeResult result, Symbol source, Symbol decision) -> void

  def merge_boundary_content: (boundary_content template_content, boundary_content dest_content, FileAligner::Boundary boundary, MergeResult result) -> void

  def build_signature_map: (Array[FileAnalysis::node_info] nodes) -> Hash[Array[untyped], Array[FileAnalysis::node_info]]

  def add_line_safe: (MergeResult result, String content, **untyped kwargs) -> void

  def handle_orphan_lines: (boundary_content template_content, boundary_content dest_content, MergeResult result) -> void

  def find_orphan_lines: (FileAnalysis analysis, Range[Integer]? line_range, Array[FileAnalysis::node_info] nodes) -> Array[Integer]
end

class SmartMerger
  attr_reader template_analysis: FileAnalysis
  attr_reader dest_analysis: FileAnalysis
  attr_reader aligner: FileAligner
  attr_reader resolver: ConflictResolver
  attr_reader result: MergeResult

  type merge_debug_result = {
    content: String,
    debug: String,
    statistics: Hash[Symbol, Integer]
  }

  # Custom signature generator return types:
  # - Array[untyped]: Used as the node's signature for matching
  # - nil: Node gets no signature (won't be matched by signature)
  # - Prism::Node or FreezeNode: Falls through to default signature computation
  def initialize: (
    String template_content,
    String dest_content,
    ?signature_generator: (^(untyped) -> (Array[untyped] | untyped | nil))?,
    ?preference: (Symbol | Hash[Symbol, Symbol]),
    ?add_template_only_nodes: bool,
    ?freeze_token: String?,
    ?max_recursion_depth: (Integer | Float),
    ?current_depth: Integer,
    ?node_typing: Hash[Symbol, untyped]?
  ) -> void

  def merge: () -> String

  def merge_with_debug: () -> merge_debug_result

  private

  def process_merge: (Array[FileAligner::Boundary] boundaries) -> void

  def build_timeline: (Array[FileAligner::Boundary] boundaries) -> Array[Hash[Symbol, untyped]]

  def process_anchor: (FileAligner::Anchor anchor) -> void

  def add_freeze_block_from_dest: (FileAligner::Anchor anchor) -> void

  def add_signature_match_from_dest: (FileAligner::Anchor anchor) -> void

  def add_exact_match_from_template: (FileAligner::Anchor anchor) -> void

  def process_boundary: (FileAligner::Boundary boundary) -> void

  # Find a node that overlaps with a line range
  def find_node_in_range: (FileAnalysis analysis, Integer start_line, Integer end_line) -> untyped?

  # Find a node at a specific line (deprecated)
  def find_node_at_line: (FileAnalysis analysis, Integer line_num) -> untyped?

  # Determine if two matching nodes should be recursively merged
  # Returns true for ClassNode, ModuleNode, SingletonClassNode, and CallNode/BeginNode
  # with blocks that contain mergeable statements.
  # Returns false if max_recursion_depth has been reached (safety valve).
  def should_merge_recursively?: (untyped? template_node, untyped? dest_node) -> bool

  # Check if a body (StatementsNode) contains statements that could be merged
  # Returns true if body contains CallNode, DefNode, ClassNode, assignments, etc.
  def body_has_mergeable_statements?: (untyped? body) -> bool

  # Check if a statement is mergeable (can generate a signature)
  # Returns true for CallNode, DefNode, ClassNode, ModuleNode, assignments, conditionals, etc.
  def mergeable_statement?: (untyped node) -> bool

  # Check if a node's body contains freeze block markers
  # @param node The node to check for freeze markers
  # @param analysis The FileAnalysis for the file containing the node
  def node_contains_freeze_blocks?: (untyped node, FileAnalysis analysis) -> bool

  # Recursively merge the body of matching class, module, or call-with-block nodes
  def merge_node_body_recursively: (untyped template_node, untyped dest_node, FileAligner::Anchor anchor) -> void

  # Extract the body content of a node (without declaration and closing 'end')
  def extract_node_body: (untyped node, FileAnalysis analysis) -> String
end   end end