#!/usr/bin/env python3
"""2nd pass: recover the reversions that the word-level locate missed (punctuation
/ hyphenation / spacing mismatches). Match on an alnum-only stream (ignoring spaces,
punctuation, hyphens, diacritics), then map back to the REAL master substring so the
search term will actually match in the LaTeX source.
"""
import fitz, re, unicodedata, json, collections, os

BASE = os.path.dirname(os.path.abspath(__file__))
M = fitz.open(os.path.join(BASE, "master.pdf"))
RANGES = json.load(open(os.path.join(BASE, "master_ranges.json")))
amap = json.load(open(os.path.join(BASE, "accent_map.json")))

def fold(s):
    s = unicodedata.normalize('NFKD', s)
    return ''.join(c for c in s if not unicodedata.combining(c)).lower()
def reaccent(s):
    return re.sub(r"[A-Za-z']+", lambda m: amap.get(fold(m.group(0)), m.group(0)), s)

def parse_revs():
    revs = collections.defaultdict(list); cur = None
    for line in open(os.path.join(BASE, "reversiones_master.md")):
        h = re.match(r'^## (\S+)', line)
        if h: cur = h.group(1)
        d = re.match(r'^- en vedabase «(.*?)»  ->  restaurar «(.*?)»', line)
        if d and cur: revs[cur].append((d.group(1), d.group(2)))
    return revs

def alnum_stream(text):
    """Return (alnum_string, idxmap) where idxmap[k] = original index of k-th alnum char."""
    out = []; idx = []
    for pos, ch in enumerate(text):
        for fc in fold(ch):
            if fc.isalnum():
                out.append(fc); idx.append(pos)
    return ''.join(out), idx

def main():
    revs = parse_revs()
    # find which corrections the word-level locate MISSED (replicate v2 logic)
    missed = []
    for k, items in revs.items():
        if k not in RANGES:
            for vd, gd in items: missed.append((k, vd, gd))
            continue
        s, e = RANGES[k]["vedabase_start"], min(RANGES[k]["vedabase_end"], len(M))
        fw_pages = {i: [fold(w[4]) for w in M[i].get_text("words")] for i in range(s, e)}
        for vd, gd in items:
            target = fold(vd).split()
            if not target: missed.append((k, vd, gd)); continue
            found = False
            for i in range(s, e):
                fw = fw_pages[i]
                for j in range(len(fw) - len(target) + 1):
                    if fw[j:j+len(target)] == target:
                        found = True; break
                if found: break
            if not found: missed.append((k, vd, gd))
    print(f"no localizadas en 1ª pasada: {len(missed)}", flush=True)

    # 2nd pass: alnum-only match per book, recover the real master substring
    recovered = []; still = []
    bookcache = {}
    for k, vd, gd in missed:
        if k not in RANGES:
            still.append((k, vd, gd)); continue
        if k not in bookcache:
            s, e = RANGES[k]["vedabase_start"], min(RANGES[k]["vedabase_end"], len(M))
            txt = ''.join(M[i].get_text() for i in range(s, e))
            bookcache[k] = (txt, *alnum_stream(txt))
        txt, astream, idx = bookcache[k]
        va = ''.join(fold(vd)[i] for i in range(len(fold(vd))) if fold(vd)[i].isalnum())
        p = astream.find(va)
        if p >= 0 and va:
            o0, o1 = idx[p], idx[p + len(va) - 1] + 1
            real = ' '.join(txt[o0:o1].split())
            recovered.append((k, real, reaccent(gd)))
        else:
            still.append((k, vd, reaccent(gd)))
    print(f"recuperadas (2ª pasada): {len(recovered)} | irrecuperables: {len(still)}", flush=True)

    # write a supplement file
    out = ["# Correcciones recuperadas (2ª pasada) — texto real del maestro", "",
           "GOLD re-acentuado · VEDA = texto EXACTO como aparece en el maestro (buscar literal)", ""]
    cur = None
    for k, real, gold in sorted(recovered):
        if k != cur: cur = k; out += ["", f"## {k}"]
        out.append(f"- buscar «{real}»  →  reemplazar por «{gold}»")
    if still:
        out += ["", "## NO RECUPERADAS (revisar a mano)"]
        for k, vd, gold in still:
            out.append(f"- [{k}] «{vd[:60]}» → «{gold[:60]}»")
    open(os.path.join(BASE, "correcciones_2a_pasada.md"), "w").write("\n".join(out))
    print(f"-> correcciones_2a_pasada.md", flush=True)
    if still[:8]:
        print("\nEjemplos irrecuperables:")
        for k, vd, gold in still[:8]: print(f"  [{k}] «{vd[:55]}»")

if __name__ == "__main__":
    main()
