module Ast
module Merge
VERSION: String

# Base error class for all merge operations
class Error < StandardError
end

# Raised when parsing fails (template or destination)
class ParseError < Error
  attr_reader errors: untyped
  attr_reader content: String?

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

# Raised when parsing the template (source) content fails
class TemplateParseError < ParseError
  def initialize: (?String? message, ?errors: untyped, ?content: String?) -> void
end

# Raised when parsing the destination content fails
class DestinationParseError < ParseError
  def initialize: (?String? message, ?errors: untyped, ?content: String?) -> void
end

# Base class for freeze block nodes in AST merge libraries.
class FreezeNode
  # Pattern configuration for freeze block markers
  MARKER_PATTERNS: Hash[Symbol, Hash[Symbol, Regexp]]

  # Default pattern when none specified
  DEFAULT_PATTERN: Symbol

  # Error raised when a freeze block has invalid structure
  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

  # Simple location struct for compatibility with AST nodes
  class Location < Struct[Integer]
    attr_accessor start_line: Integer
    attr_accessor end_line: Integer

    def cover?: (Integer line) -> bool
  end

  # Class methods
  def self.register_pattern: (Symbol name, start: Regexp, end_pattern: Regexp) -> Hash[Symbol, Regexp]
  def self.start_pattern: (?Symbol pattern_type) -> Regexp
  def self.end_pattern: (?Symbol pattern_type) -> Regexp
  def self.freeze_start?: (String? line, ?Symbol pattern_type) -> bool
  def self.freeze_end?: (String? line, ?Symbol pattern_type) -> bool
  def self.pattern_types: () -> Array[Symbol]

  # Instance attributes
  attr_reader start_line: Integer
  attr_reader end_line: Integer
  attr_reader content: String?
  attr_reader start_marker: String?
  attr_reader end_marker: String?
  attr_reader pattern_type: Symbol

  def initialize: (
    start_line: Integer,
    end_line: Integer,
    ?start_marker: String?,
    ?end_marker: String?,
    ?pattern_type: Symbol
  ) -> void

  def location: () -> Location
  def slice: () -> String?
  def freeze_node?: () -> bool
  def signature: () -> Array[untyped]
  def inspect: () -> String
  def to_s: () -> String

  private

  def validate_line_order!: () -> void
end

# Debug logging module
module DebugLogger
  def self.env_var_name: () -> String
  def self.env_var_name=: (String name) -> String
  def self.log_prefix: () -> String
  def self.log_prefix=: (String prefix) -> String
  def self.enabled?: () -> bool
  def self.debug: (*untyped args) -> void
  def self.info: (*untyped args) -> void
  def self.warning: (*untyped args) -> void
  def self.time: (String label) { () -> untyped } -> untyped
  def self.log_node: (untyped node, ?label: String) -> void
  def self.extract_lines: (untyped node) -> String
  def self.safe_type_name: (untyped node) -> String
end

# Base module for file analysis classes
module FileAnalysisBase
  # Required instance attributes (must be defined by including class)
  attr_reader statements: Array[untyped]
  attr_reader lines: Array[String]
  attr_reader signature_generator: (^(untyped) -> (Array[untyped] | untyped | nil))?

  # Get all freeze blocks from statements
  def freeze_blocks: () -> Array[FreezeNode]

  # Check if a line is within a freeze block
  def in_freeze_block?: (Integer line_num) -> bool

  # Get the freeze block containing the given line
  def freeze_block_at: (Integer line_num) -> FreezeNode?

  # Get structural signature for a statement at given index
  def signature_at: (Integer index) -> Array[untyped]?

  # Get a specific line (1-indexed)
  def line_at: (Integer line_num) -> String?

  # Get a normalized line (whitespace-trimmed)
  def normalized_line: (Integer line_num) -> String?

  # Generate signature for a node
  def generate_signature: (untyped node) -> Array[untyped]?

  # Check if a value represents a fallthrough node
  def fallthrough_node?: (untyped value) -> bool

  # Compute default signature for a node (abstract - must be implemented)
  def compute_node_signature: (untyped node) -> Array[untyped]?
end

# Base merge result tracking
class MergeResult
  DECISION_KEPT_TEMPLATE: Symbol
  DECISION_KEPT_DEST: Symbol
  DECISION_MERGED: Symbol
  DECISION_ADDED: Symbol
  DECISION_FREEZE_BLOCK: Symbol
  DECISION_REPLACED: Symbol
  DECISION_APPENDED: Symbol

  attr_reader decisions: Array[Hash[Symbol, untyped]]

  def initialize: () -> void
  def track_decision: (
    untyped node,
    Symbol decision,
    ?reason: String?
  ) -> void
end

# Configuration object for SmartMerger options
class MergerConfig
  VALID_PREFERENCES: Array[Symbol]

  attr_reader signature_match_preference: Symbol | Hash[Symbol, Symbol]
  attr_reader node_splitter: Hash[Symbol, untyped]?
  attr_reader add_template_only_nodes: add_template_only_nodes_type
  attr_reader freeze_token: String?
  attr_reader signature_generator: (^(untyped) -> (Array[untyped] | untyped | nil))?

  def initialize: (
    ?signature_match_preference: (Symbol | Hash[Symbol, Symbol]),
    ?add_template_only_nodes: add_template_only_nodes_type,
    ?freeze_token: String?,
    ?signature_generator: (^(untyped) -> (Array[untyped] | untyped | nil))?,
    ?node_splitter: Hash[Symbol, untyped]?
  ) -> void

  def prefer_destination?: () -> bool
  def prefer_template?: () -> bool
  def to_h: (?default_freeze_token: String?) -> Hash[Symbol, untyped]
  def with: (**untyped options) -> MergerConfig

  def self.destination_wins: (?freeze_token: String?, ?signature_generator: (^(untyped) -> (Array[untyped] | untyped | nil))?, ?node_splitter: Hash[Symbol, untyped]?) -> MergerConfig
  def self.template_wins: (?freeze_token: String?, ?signature_generator: (^(untyped) -> (Array[untyped] | untyped | nil))?, ?node_splitter: Hash[Symbol, untyped]?) -> MergerConfig

  private

  def validate_preference!: (Symbol preference) -> void
end

# Type alias for node typing callables
type node_typing_callable = ^(untyped) -> untyped?
type node_typing_hash = Hash[Symbol | String, node_typing_callable]
type preference_type = Symbol | Hash[Symbol, Symbol]

# Type alias for add_template_only_nodes filter
# Can be: Boolean, or callable that receives (node, entry) and returns truthy/falsey
# Entry hash contains: { template_node:, signature:, template_index:, dest_index: nil }
type add_template_only_filter = ^(untyped node, Hash[Symbol, untyped] entry) -> boolish
type add_template_only_nodes_type = bool | add_template_only_filter

# Abstract base class for SmartMerger implementations
class SmartMergerBase
  include Detector::Mergeable

  attr_reader template_content: String
  attr_reader dest_content: String
  attr_reader template_analysis: untyped
  attr_reader dest_analysis: untyped
  attr_reader resolver: untyped
  attr_reader result: untyped
  attr_reader preference: preference_type
  attr_reader add_template_only_nodes: add_template_only_nodes_type
  attr_reader freeze_token: String
  attr_reader signature_generator: (^(untyped) -> (Array[untyped] | untyped | nil))?
  attr_reader match_refiner: untyped?
  attr_reader node_typing: node_typing_hash?

  def initialize: (
    String template_content,
    String dest_content,
    ?signature_generator: (^(untyped) -> (Array[untyped] | untyped | nil))?,
    ?preference: preference_type,
    ?add_template_only_nodes: add_template_only_nodes_type,
    ?freeze_token: String?,
    ?match_refiner: untyped?,
    ?regions: Array[Hash[Symbol, untyped]]?,
    ?region_placeholder: String?,
    ?node_typing: node_typing_hash?,
    **untyped format_options
  ) -> void

  def merge: () -> String
  def merge_result: () -> untyped

  # Class method for convenient merging
  def self.merge: (
    String template_content,
    String dest_content,
    **untyped options
  ) -> String

  private

  # Abstract methods that subclasses must implement
  def analysis_class: () -> Class
  def perform_merge: () -> untyped

  # Optional hooks for subclasses
  def default_freeze_token: () -> String
  def resolver_class: () -> Class?
  def result_class: () -> Class?
  def aligner_class: () -> Class?
  def build_analysis_options: () -> Hash[Symbol, untyped]
  def build_resolver_options: () -> Hash[Symbol, untyped]
  def build_full_analysis_options: (Symbol source) -> Hash[Symbol, untyped]
  def update_result_content: (untyped result, String content) -> void
  def template_parse_error_class: () -> Class
  def destination_parse_error_class: () -> Class
end

# Abstract base class for ConflictResolver implementations
class ConflictResolverBase
  # Decision constants
  DECISION_KEPT_TEMPLATE: Symbol
  DECISION_KEPT_DEST: Symbol
  DECISION_MERGED: Symbol
  DECISION_ADDED: Symbol
  DECISION_FREEZE_BLOCK: Symbol
  DECISION_APPENDED: Symbol
  DECISION_REPLACED: Symbol

  attr_reader strategy: Symbol
  attr_reader preference: preference_type
  attr_reader template_analysis: untyped
  attr_reader dest_analysis: untyped
  attr_reader add_template_only_nodes: bool
  attr_reader match_refiner: untyped?

  def initialize: (
    strategy: Symbol,
    preference: preference_type,
    template_analysis: untyped,
    dest_analysis: untyped,
    ?add_template_only_nodes: bool,
    ?match_refiner: untyped?,
    **untyped options
  ) -> void

  def resolve: (*untyped args, **untyped kwargs) -> untyped

  # Get preference for a specific node (supports Hash preference)
  def preference_for: (untyped node) -> Symbol

  private

  def validate_preference!: (preference_type preference) -> void
  def resolve_node_pair: (untyped template_node, untyped dest_node, **untyped kwargs) -> untyped
  def resolve_batch: (*untyped args) -> untyped
  def resolve_boundary: (*untyped args) -> untyped
end

# Abstract base class for MergeResult implementations
class MergeResultBase
  # Decision constants
  DECISION_KEPT_TEMPLATE: Symbol
  DECISION_KEPT_DEST: Symbol
  DECISION_MERGED: Symbol
  DECISION_ADDED: Symbol
  DECISION_FREEZE_BLOCK: Symbol
  DECISION_APPENDED: Symbol
  DECISION_REPLACED: Symbol

  attr_reader template_analysis: untyped?
  attr_reader dest_analysis: untyped?
  attr_reader lines: Array[String]
  attr_reader decisions: Array[Hash[Symbol, untyped]]
  attr_reader conflicts: Array[Hash[Symbol, untyped]]
  attr_reader frozen_blocks: Array[untyped]
  attr_reader stats: Hash[Symbol, untyped]

  def initialize: (
    ?template_analysis: untyped?,
    ?dest_analysis: untyped?,
    ?conflicts: Array[Hash[Symbol, untyped]],
    ?frozen_blocks: Array[untyped],
    ?stats: Hash[Symbol, untyped],
    **untyped options
  ) -> void

  def content: () -> Array[String]
  def content?: () -> bool
  def content_string: () -> String
  def to_s: () -> String
  def success?: () -> bool
  def conflicts?: () -> bool
  def track_decision: (Symbol decision, Symbol source, **untyped metadata) -> void
end

# Abstract base class for MatchRefiner implementations
class MatchRefinerBase
  # Default similarity threshold
  DEFAULT_THRESHOLD: Float

  attr_reader threshold: Float
  attr_reader node_types: Array[Symbol]?

  def initialize: (
    ?threshold: Float,
    ?node_types: Array[Symbol]?,
    **untyped options
  ) -> void

  # Find matches between unmatched nodes
  def call: (
    Array[untyped] template_nodes,
    Array[untyped] dest_nodes,
    ?Hash[Symbol, untyped] context
  ) -> Array[MatchResult]

  # Compute similarity score between two nodes
  def similarity: (untyped template_node, untyped dest_node) -> Float

  # Check if a node matches the configured types
  def matches_type?: (untyped node) -> bool

  private

  # Levenshtein distance for string similarity
  def levenshtein_distance: (String s1, String s2) -> Integer
  def string_similarity: (String s1, String s2) -> Float
end

# Result of a match refinement operation
class MatchResult
  attr_reader template_node: untyped
  attr_reader dest_node: untyped
  attr_reader score: Float
  attr_reader metadata: Hash[Symbol, untyped]

  def initialize: (
    template_node: untyped,
    dest_node: untyped,
    score: Float,
    ?metadata: Hash[Symbol, untyped]
  ) -> void

  def to_h: () -> Hash[Symbol, untyped]
end

# Detector namespace for region detection and merging
module Detector
  # Represents a detected region within a document
  class Region < Struct[untyped]
    attr_accessor type: Symbol
    attr_accessor content: String
    attr_accessor start_line: Integer
    attr_accessor end_line: Integer
    attr_accessor delimiters: Array[String]?
    attr_accessor metadata: Hash[Symbol, untyped]?

    def line_range: () -> Range[Integer]
    def line_count: () -> Integer
    def full_text: () -> String
    def contains_line?: (Integer line) -> bool
    def overlaps?: (Region other) -> bool
    def to_s: () -> String
    def inspect: () -> String
  end

  # Abstract base class for region detectors
  class Base
    def region_type: () -> Symbol
    def detect_all: (String source) -> Array[Region]
    def strip_delimiters?: () -> bool
    def name: () -> String
    def inspect: () -> String

    private

    def build_region: (
      type: Symbol,
      content: String,
      start_line: Integer,
      end_line: Integer,
      ?delimiters: Array[String]?,
      ?metadata: Hash[Symbol, untyped]?
    ) -> Region
  end

  # Detects fenced code blocks
  class FencedCodeBlock < Base
    attr_reader language: String
    attr_reader aliases: Array[String]

    def initialize: (String language, ?aliases: Array[String]) -> void
    def self.ruby: () -> FencedCodeBlock
    def self.yaml: () -> FencedCodeBlock
    def self.json: () -> FencedCodeBlock
    def self.bash: () -> FencedCodeBlock
  end

  # Detects YAML frontmatter
  class YamlFrontmatter < Base
    FRONTMATTER_PATTERN: Regexp
  end

  # Detects TOML frontmatter
  class TomlFrontmatter < Base
    FRONTMATTER_PATTERN: Regexp
  end

  # Mixin for region-aware merging
  module Mergeable
    DEFAULT_PLACEHOLDER_PREFIX: String
    DEFAULT_PLACEHOLDER_SUFFIX: String

    # Configuration for a region type
    class Config < Struct[untyped]
      attr_accessor detector: Base
      attr_accessor merger_class: Class?
      attr_accessor merger_options: Hash[Symbol, untyped]
      attr_accessor regions: Array[Hash[Symbol, untyped]]
    end

    # Extracted region with placeholder
    class ExtractedRegion < Struct[untyped]
      attr_accessor region: Region
      attr_accessor config: Config
      attr_accessor placeholder: String
      attr_accessor merged_content: String?
    end

    def regions_configured?: () -> bool
    def setup_regions: (regions: Array[Hash[Symbol, untyped]], ?region_placeholder: String?) -> void
    def extract_template_regions: (String content) -> String
    def extract_dest_regions: (String content) -> String
    def substitute_merged_regions: (String content) -> String
  end
end

# Recipe namespace for YAML-based merge recipes
module Recipe
  # Recipe configuration loaded from YAML
  class Config
    attr_reader name: String
    attr_reader description: String?
    attr_reader template_path: String
    attr_reader targets: Array[String]
    attr_reader injection: Hash[String, untyped]
    attr_reader merge_options: Hash[String, untyped]
    attr_reader when_missing: Symbol
    attr_reader recipe_path: String?

    def self.load: (String path) -> Config
    def initialize: (Hash[String, untyped] config, ?recipe_path: String?) -> void
    def anchor_config: () -> Hash[String, untyped]
    def boundary_config: () -> Hash[String, untyped]?
    def position: () -> Symbol
    def preference: () -> Symbol
    def add_missing?: () -> bool
    def replace_mode?: () -> bool
  end

  # Executes recipes against target files
  class Runner
    # Result of processing a single file
    class Result < Struct[untyped]
      attr_accessor path: String
      attr_accessor relative_path: String
      attr_accessor status: Symbol
      attr_accessor changed: bool
      attr_accessor has_anchor: bool
      attr_accessor message: String?
      attr_accessor stats: Hash[Symbol, untyped]?
      attr_accessor error: String?
    end

    attr_reader recipe: Config
    attr_reader dry_run: bool
    attr_reader verbose: bool
    attr_reader parser: Symbol
    attr_reader base_dir: String
    attr_reader results: Array[Result]

    def initialize: (
      Config recipe,
      ?dry_run: bool,
      ?verbose: bool,
      ?parser: Symbol,
      ?base_dir: String
    ) -> void

    def run: () ?{ (Result) -> void } -> Array[Result]
    def summary: () -> Hash[Symbol, Integer]
    def summary_table: () -> Array[Hash[Symbol, untyped]]
  end

  # Loads Ruby scripts referenced by recipes
  class ScriptLoader
    attr_reader base_dir: String?
    attr_reader cache: Hash[String, untyped]

    def initialize: (?recipe_path: String?, ?base_dir: String?) -> void
    def load_callable: (String script_ref) -> (^(*untyped) -> untyped)?
    def resolve_path: (String script_ref) -> String?

    private

    def load_from_file: (String path) -> untyped
    def parse_inline_lambda: (String code) -> (^(*untyped) -> untyped)?
  end
end

# Module for node typing support
module NodeTyping
  # Wrapper class for typed nodes
  class Wrapper
    attr_reader node: untyped
    attr_reader merge_type: Symbol

    def initialize: (untyped node, Symbol merge_type) -> void
    def method_missing: (Symbol method, *untyped args) ?{ (*untyped) -> untyped } -> untyped
    def respond_to_missing?: (Symbol method, ?bool include_private) -> bool
    def typed_node?: () -> bool
    def unwrap: () -> untyped
    def ==: (untyped other) -> bool
    def hash: () -> Integer
    def eql?: (untyped other) -> bool
    def inspect: () -> String
  end

  # Wrapper for frozen AST nodes that includes Freezable behavior
  class FrozenWrapper < Wrapper
    include Freezable

    def initialize: (untyped node, ?Symbol merge_type) -> void
    def frozen_node?: () -> bool
    def slice: () -> String
    def signature: () -> Array[untyped]
    def inspect: () -> String
  end

  # Thread-safe backend registration and type normalization module.
  # Extended by format-specific normalizers (e.g., Toml::Merge::NodeTypeNormalizer).
  module Normalizer
    @normalizer_mutex: Mutex
    @backend_mappings: Hash[Symbol, Hash[Symbol, Symbol]]

    # Called when this module is extended into another module
    def self.extended: (Module base) -> void

    # Configure initial backend mappings
    # @param mappings Keyword args where keys are backend symbols and values are type mapping hashes
    def configure_normalizer: (**untyped) -> void

    # Register type mappings for a new backend (thread-safe)
    def register_backend: (Symbol backend, Hash[Symbol, Symbol] mappings) -> void

    # Get the canonical type for a backend-specific type
    def canonical_type: (Symbol | String | nil backend_type, ?Symbol? backend) -> Symbol?

    # Wrap a node with its canonical type as merge_type
    def wrap: (untyped node, Symbol backend) -> Wrapper

    # Get all registered backends
    def registered_backends: () -> Array[Symbol]

    # Check if a backend is registered
    def backend_registered?: (Symbol | String backend) -> bool

    # Get the mappings for a specific backend
    def mappings_for: (Symbol backend) -> Hash[Symbol, Symbol]?

    # Get all canonical types across all backends
    def canonical_types: () -> Array[Symbol]
  end

  def self.with_merge_type: (untyped node, Symbol merge_type) -> Wrapper
  def self.frozen: (untyped node, ?Symbol merge_type) -> FrozenWrapper
  def self.frozen_node?: (untyped node) -> bool
  def self.typed_node?: (untyped node) -> bool
  def self.merge_type_for: (untyped node) -> Symbol?
  def self.unwrap: (untyped node) -> untyped
  def self.process: (untyped node, node_typing_hash? typing_config) -> untyped?
  def self.validate!: (node_typing_hash? typing_config) -> void
end   end end