"""Word-level matching with shape compression + Levenshtein."""

import unicodedata
from rapidfuzz import fuzz
from harness import evaluate

MULTI_CHAR_RULES = [
    ("rn", "m"),
    ("nn", "m"),
    ("ri", "n"),
    ("cl", "d"),
    ("vv", "w"),
    ("ii", "u"),
]
MULTI_CHAR_RULES.sort(key=lambda x: len(x[0]), reverse=True)

SHAPE_MAP = {}
for ch in "il1I|!tfj":
    SHAPE_MAP[ch] = "I"
for ch in "oO0cCeQa":
    SHAPE_MAP[ch] = "O"
for ch in "mwMW":
    SHAPE_MAP[ch] = "M"
for ch in "nuvNUV":
    SHAPE_MAP[ch] = "N"
for ch in "bhk6":
    SHAPE_MAP[ch] = "B"
for ch in "pqgy9":
    SHAPE_MAP[ch] = "P"
for ch in "dDPRB":
    SHAPE_MAP[ch] = "D"
for ch in "xXzZK":
    SHAPE_MAP[ch] = "X"
for ch in "sS5$":
    SHAPE_MAP[ch] = "S"
for ch in "-_~=":
    SHAPE_MAP[ch] = "H"
for ch in "234":
    SHAPE_MAP[ch] = "2"
for ch in "78":
    SHAPE_MAP[ch] = "7"
SHAPE_MAP["r"] = "R"


def shape_compress(text):
    text = unicodedata.normalize("NFKC", text).lower()
    for pattern, replacement in MULTI_CHAR_RULES:
        text = text.replace(pattern, replacement)
    return "".join(SHAPE_MAP.get(ch, ch) for ch in text)


def word_score(query, candidate):
    q_words = query.lower().split()
    c_words = candidate.lower().split()

    if not q_words or not c_words:
        return 0.0

    total = 0.0
    for qw in q_words:
        sq = shape_compress(qw)
        best = 0.0
        for cw in c_words:
            sc = shape_compress(cw)
            s = fuzz.ratio(sq, sc) / 100.0
            if s > best:
                best = s
        total += best

    return total / len(q_words)


if __name__ == "__main__":
    evaluate(word_score, name="09c - Word-level Shape Compression + Levenshtein")
