"""Text normalization for fuzzy matching.

Transforms raw text into a canonical form suitable for comparison:
lowercase → synonym replacement → accent removal → punctuation removal →
tokenize → stopword removal → sort → join.

Sorting tokens makes the comparison order-independent, so
"blue tape 50mm" and "50mm tape blue" normalize to the same string.
"""

from __future__ import annotations

import re
import unicodedata
from typing import Dict, Set


def normalize_text(
    text: str,
    synonym_map: Dict[str, str] | None = None,
    stopwords: Set[str] | None = None,
) -> str:
    """Normalize *text* for fuzzy matching.

    Args:
        text: Raw input text to normalize.
        synonym_map: Optional mapping of variant spellings to canonical
            forms. Applied longest-first so multi-word synonyms match
            before their sub-tokens.
        stopwords: Optional set of tokens to remove after tokenization.

    Returns:
        A normalized, sorted, space-joined string ready for fuzzy
        comparison. Returns empty string for non-string input.

    Example::

        >>> normalize_text("Noppenfolie 50x60 transparant", {"noppenfolie": "bubbel"}, {"de"})
        '50x60 bubbel transparant'
    """
    if not isinstance(text, str):
        return ""

    processed = text.lower()

    # Synonym replacement (longest keys first so multi-word synonyms match)
    if synonym_map:
        for key in sorted(synonym_map, key=len, reverse=True):
            processed = processed.replace(key, synonym_map[key])

    # Unicode / accent normalization
    normalised = unicodedata.normalize("NFKD", processed)
    without_accents = "".join(
        c for c in normalised if unicodedata.category(c) != "Mn"
    )

    # Replace punctuation with spaces (keeps dimension separators like 'x')
    sanitised = re.sub(r"[^\w\s]", " ", without_accents)

    # Tokenize and remove stopwords
    tokens = sanitised.split()
    if stopwords:
        tokens = [t for t in tokens if t not in stopwords]

    # Sort for order-independent comparison
    tokens.sort()

    return " ".join(tokens)
