from qhelium._impl.util.xpath import lower, replace_nbsp
from difflib import SequenceMatcher

class MatchType:
	def xpath(self, value, text):
		raise NotImplementedError()
	def text(self, value, text):
		raise NotImplementedError()

class PREFIX_IGNORE_CASE(MatchType):
	def xpath(self, value, text):
		if not text:
			return ''
		# Asterisks '*' are sometimes used to mark required fields. Eg.:
		# <label for="title"><span class="red-txt">*</span> Title:</label>
		# The starts-with filter below would be too strict to include such
		# matches. To get around this, we ignore asterisks unless the searched
		# text itself contains one.
		if '*' in text:
			strip_asterisks = value
		else:
			strip_asterisks = "translate(%s, '*', '')" % value

		# if text contains apostrophes (single quotes) then they need to be
		# treated with care
		if "'" in text:
			text = "concat('%s')" % ("',\"'\",'".join(text.split("'")))
		else:
			text = "'%s'" % text

		return "starts-with(normalize-space(%s), %s)" % (
			lower(replace_nbsp(strip_asterisks)), text.lower()
		)
	def text(self, value, text):
		if not text:
			return True
		return value.lower().lstrip().startswith(text.lower())

class FUZZY_MATCH(MatchType):
	def xpath(self, value, text):
		if not text:
			return ''
		# We can't do fuzzy matching in XPath, so we'll match any text content
		# and filter later in Python
		return "string-length(normalize-space(%s)) > 0" % value

	def get_ratio(self, value, text):
		"""Calculate the fuzzy match ratio between two strings."""
		if not text or not value:
			return 0
		return SequenceMatcher(None, value, text).ratio()

	def text(self, value, text):
		if not text:
			return True
		# Use SequenceMatcher to calculate similarity ratio
		ratio = self.get_ratio(value.lower(), text.lower())
		# Any ratio is acceptable since we'll sort by ratio later
		return ratio > 0.5