"""Iteration: First-char masking to counter JW prefix bias.

GPT's alternative to reverse JW: compute JW on the strings with
first character removed, then blend with full JW.
"""

import re
import unicodedata
from rapidfuzz.distance import JaroWinkler
from harness import evaluate

PUNCT_RE = re.compile(r'^[^a-zA-Z0-9]+|[^a-zA-Z0-9]+$')

def strip_punct(t):
    return PUNCT_RE.sub('', t)

def norm(t):
    return strip_punct(unicodedata.normalize("NFKC", t)).lower()

def length_ratio(a, b):
    la, lb = len(a), len(b)
    if la == 0 or lb == 0:
        return 0.0
    return min(la, lb) / max(la, lb)


def score_first_char_mask(query, candidate):
    """Max(JW_full, JW_no_first_char * 0.98)."""
    q, c = norm(query), norm(candidate)
    if not q or not c:
        return 0.0
    jw_full = JaroWinkler.similarity(q, c)
    if len(q) > 1 and len(c) > 1:
        jw_masked = JaroWinkler.similarity(q[1:], c[1:]) * 0.98
    else:
        jw_masked = 0.0
    return max(jw_full, jw_masked)


def score_first_char_mask_len(query, candidate):
    """Max(JW, JW_masked*0.98) * length penalty."""
    q, c = norm(query), norm(candidate)
    if not q or not c:
        return 0.0
    jw_full = JaroWinkler.similarity(q, c)
    if len(q) > 1 and len(c) > 1:
        jw_masked = JaroWinkler.similarity(q[1:], c[1:]) * 0.98
    else:
        jw_masked = 0.0
    lr = length_ratio(q, c)
    return max(jw_full, jw_masked) * (0.8 + 0.2 * lr)


if __name__ == "__main__":
    evaluate(score_first_char_mask, name="17a - First-char Mask JW")
    evaluate(score_first_char_mask_len, name="17b - First-char Mask JW * len")
