extends RefCounted
class_name FuzzySearch

## Simple fuzzy matching algorithm for text search
## Returns a score between 0 (no match) and 1 (perfect match)

static func match(query: String, target: String) -> float:
	if query.is_empty():
		return 1.0
	
	query = query.to_lower()
	target = target.to_lower()
	
	# Exact match
	if target == query:
		return 1.0
	
	# Contains query as substring
	if target.find(query) != -1:
		return 0.9
	
	# Fuzzy match - all characters in query must appear in order in target
	var query_idx: int = 0
	var target_idx: int = 0
	var matches: int = 0
	var consecutive_bonus: float = 0.0
	var last_match_idx: int = -1
	
	while query_idx < query.length() and target_idx < target.length():
		if query[query_idx] == target[target_idx]:
			matches += 1
			if last_match_idx != -1 and target_idx == last_match_idx + 1:
				consecutive_bonus += 0.1
			last_match_idx = target_idx
			query_idx += 1
		target_idx += 1
	
	# All characters matched
	if query_idx == query.length():
		var base_score: float = float(matches) / float(query.length())
		var bonus: float = min(consecutive_bonus, 0.3)  # Cap bonus at 0.3
		return base_score * 0.7 + bonus
	
	return 0.0

## Sort an array of items by fuzzy match score
## items should be Dictionary with at least a "name" or specified key field
static func sort_by_match(query: String, items: Array, key: String = "name") -> Array:
	if query.is_empty():
		return items.duplicate()
	
	var scored: Array[Dictionary] = []
	for item in items:
		var item_name: String = item.get(key, "")
		var score: float = match(query, item_name)
		if score > 0:
			scored.append({"item": item, "score": score})
	
	# Sort by score descending
	scored.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
		return a["score"] > b["score"]
	)
	
	return scored.map(func(s): return s["item"])
