# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2025-2026, by Samuel Williams.

module Markly
	module Renderer
		# Extracts headings from a markdown document with unique anchor IDs.
		# Handles duplicate heading text by appending counters (e.g., "deployment", "deployment-2", "deployment-3").
		class Headings
			# Initializes an empty heading-anchor registry.
			def initialize
				@ids = {}
			end
			
			# Generate a unique anchor for a node.
			#
			# @parameter node [Markly::Node] The heading node.
			# @returns [String] A unique anchor ID.
			def anchor_for(node)
				base = base_anchor_for(node)
				
				if @ids.key?(base)
					@ids[base] += 1
					"#{base}-#{@ids[base]}"
				else
					@ids[base] = 1
					base
				end
			end
			
			# Extract all headings from a document root with unique anchors.
			#
			# @parameter root [Markly::Node] The document root node.
			# @parameter min_level [Integer] The minimum heading level to extract.
			# @parameter max_level [Integer] The maximum heading level to extract.
			# @returns [Array(Heading)] The extracted headings with unique anchors.
			def extract(root, min_level: 1, max_level: 6)
				headings = []
				root.walk do |node|
					if node.type == :header
						level = node.header_level
						next if level < min_level || level > max_level
						
						headings << Heading.new(
							node: node,
							level: level,
							text: node.to_plaintext.chomp,
							anchor: anchor_for(node)
						)
					end
				end
				headings
			end
			
			# Extract all headings using a new heading-anchor registry.
			#
			# @parameter root [Markly::Node] The document root node.
			# @parameter min_level [Integer] The minimum heading level to extract.
			# @parameter max_level [Integer] The maximum heading level to extract.
			# @returns [Array(Heading)] The extracted headings with unique anchors.
			def self.extract(root, min_level: 1, max_level: 6)
				new.extract(root, min_level: min_level, max_level: max_level)
			end
			
			private
			
			# Generate a base anchor from a node's text content.
			#
			# @parameter node [Markly::Node] The heading node.
			# @returns [String] The lowercase, hyphenated base anchor.
			def base_anchor_for(node)
				text = node.to_plaintext.chomp.downcase
				text.gsub(/\s+/, "-")
			end
		end
		
		# Represents a heading extracted from a document.
		#
		# The structure contains the original `node`, its `level`, its plain-text
		# `text`, and its unique `anchor`.
		#
		# @constant [Class] The heading data class.
		Heading = Struct.new(:node, :level, :text, :anchor, keyword_init: true)
	end
end
