#!/usr/bin/env python3
"""Browsable HTML for Phase 2 review.

GOLD = original scan (truth, red). VEDA = vedabase (audited, green).
- Confidence tiers (alta/media/baja), sorted most-probable-first per book.
- Checkbox = SELECT (the good ones to revert). Marked rows are kept in the export.
- Per-book "Marcar todo" / "Quitar" buttons for bulk selection.
- Export = the SELECTED changes → vedabase_reversiones.md. localStorage persistence.
"""
import json, glob, os, re, difflib, html

BASE = os.path.dirname(os.path.abspath(__file__))
EXCLUDE = {"tlc", "ejop"}
GARBAGE = re.compile(r'[<>=~|{}\\^_]|[A-Za-z][0-9]|[0-9][A-Za-z]')
FUNC = set("the a an of to and in is was are be that this it as for on with by at "
           "from or not but his her him he she they we you i my our their its "
           "which who had has have been will would can could so if then than there "
           "into out up no all any one two also did do does made after over only "
           "such like most more new now me even some them when".split())
try:
    WORDS = set(w.strip().lower() for w in open("/usr/share/dict/words"))
except Exception:
    WORDS = set()

def clean(s): return ' '.join(str(s).split())
def is_garbled(s):
    if GARBAGE.search(s): return True
    if not re.findall(r'[A-Za-z]{2,}', s) and re.search(r'[A-Za-z]', s): return True
    return False
def char_ratio(a, b):
    a2 = re.sub(r'[^a-z]', '', a.lower()); b2 = re.sub(r'[^a-z]', '', b.lower())
    if not a2 and not b2: return 1.0
    return difflib.SequenceMatcher(None, a2, b2).ratio()
def classify(o, v):
    o, v = clean(o), clean(v)
    if not re.search(r'[A-Za-z]', o) and not re.search(r'[A-Za-z]', v): return "trivial"
    if is_garbled(o): return "noise"
    if char_ratio(o, v) >= 0.82: return "trivial"
    return "real"
def confidence(o, v):
    ot = re.findall(r"[A-Za-z']+", o); vt = re.findall(r"[A-Za-z']+", v)
    def suspicious(toks):
        return any(t.lower() not in WORDS and not t[0].isupper() and len(t) > 1 for t in toks)
    if WORDS and (suspicious(ot) or suspicious(vt)):
        return ("media", 55)
    co = {t.lower() for t in ot if t.lower() not in FUNC}
    cv = {t.lower() for t in vt if t.lower() not in FUNC}
    if co ^ cv:
        return ("alta", 92)
    return ("baja", 30)
def esc(s): return html.escape(clean(s))

def main():
    import sys
    reports_dir = sys.argv[1] if len(sys.argv) > 1 else "reports"
    out_name = sys.argv[2] if len(sys.argv) > 2 else "PHASE2_review.html"
    books = []
    for f in sorted(glob.glob(os.path.join(BASE, reports_dir, "*_diffs.json"))):
        key = os.path.basename(f).replace("_diffs.json", "")
        if key in EXCLUDE: continue
        d = json.load(open(f))
        reals = []
        for x in d.get("differences", []):
            if classify(x.get("original",""), x.get("vedabase","")) != "real":
                continue
            tier, score = confidence(clean(x.get("original","")), clean(x.get("vedabase","")))
            reals.append((score, tier, x))
        reals.sort(key=lambda r: -r[0])
        if reals:
            books.append((len(reals), key, d["book"], reals))
    books.sort(reverse=True)
    total = sum(b[0] for b in books)
    n_alta = sum(1 for _,_,_,rs in books for s,t,_ in rs if t == "alta")

    rows = "".join(
        f'<tr><td><a href="#{k}">{k}</a></td><td>{esc(name)}</td><td class="num">{n}</td>'
        f'<td class="num">{sum(1 for s,t,_ in rs if t=="alta")}</td></tr>'
        for n, k, name, rs in books)

    sections = []
    for n, k, name, reals in books:
        items = []
        for i, (score, tier, x) in enumerate(reals):
            cid = f"{k}_{i}"
            cb = esc(x.get("context_before","")); ca = esc(x.get("context_after",""))
            o = esc(x.get("original","")); v = esc(x.get("vedabase",""))
            items.append(
                f'<label class="d t-{tier}" data-id="{cid}">'
                f'<input type="checkbox" class="sel" data-id="{cid}" data-book="{k}" '
                f'data-gold="{o}" data-veda="{v}">'
                f'<span class="body"><span class="badge {tier}">{tier}</span> '
                f'<span class="ctx">…{cb}</span> '
                f'<span class="gold">{o}</span> <span class="arr">→</span> '
                f'<span class="veda">{v}</span> <span class="ctx">{ca}…</span></span></label>')
        sections.append(
            f'<section id="{k}" class="book" data-book="{k}"><h2>{k} — {esc(name)} '
            f'<span class="count">{n}</span> '
            f'<button class="mini markall" data-book="{k}">Marcar todo</button> '
            f'<button class="mini unmarkall" data-book="{k}">Quitar</button></h2>'
            f'{"".join(items)}</section>')

    doc = f"""<!doctype html><html lang="es"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Vedabase — Fase 2: cambios editoriales</title>
<style>
:root{{--gold:#b3261e;--veda:#1a7f37;}}
*{{box-sizing:border-box}}
body{{font:15px/1.55 -apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;margin:0;color:#1c1c1e;background:#f6f6f7}}
header{{position:sticky;top:0;background:#fff;border-bottom:1px solid #e2e2e4;padding:12px 20px;z-index:5}}
header h1{{margin:0 0 6px;font-size:17px}}
.meta{{color:#666;font-size:13px}}
.bar{{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-top:8px}}
#q{{flex:1;min-width:200px;max-width:360px;padding:8px 10px;border:1px solid #ccc;border-radius:8px;font-size:14px}}
button{{padding:7px 12px;border:1px solid #ccc;border-radius:8px;background:#fff;font-size:13px;cursor:pointer}}
button:hover{{background:#f0f0f4}} button.on{{background:#e7efff;border-color:#9bf}}
button.mini{{padding:2px 8px;font-size:11px;margin-left:6px;font-weight:400}}
.tag{{padding:7px 12px;border-radius:8px;background:#f0f0f4;font-size:13px}}
#cnt{{font-weight:600;color:#1a7f37}}
main{{max-width:1040px;margin:0 auto;padding:20px}}
table{{border-collapse:collapse;width:100%;background:#fff;border-radius:10px;overflow:hidden;box-shadow:0 1px 2px rgba(0,0,0,.06)}}
th,td{{padding:7px 12px;text-align:left;border-bottom:1px solid #eee;font-size:13px}}
th{{background:#fafafa}} td.num,th.num{{text-align:right;font-variant-numeric:tabular-nums}}
.book{{background:#fff;border-radius:10px;padding:14px 18px;margin:16px 0;box-shadow:0 1px 2px rgba(0,0,0,.06)}}
.book h2{{font-size:15px;margin:0 0 10px}}
.count{{background:#eef;color:#33a;border-radius:20px;padding:1px 9px;font-size:12px;margin-left:6px}}
.d{{display:flex;gap:10px;align-items:flex-start;padding:7px 8px;border-bottom:1px solid #f1f1f1;font-size:14px;cursor:pointer}}
.d:hover{{background:#fafaff}}
.d input{{margin-top:4px;width:16px;height:16px;flex:none;cursor:pointer}}
.d.sel{{background:#eefcf1}} .d.sel .veda{{text-decoration:underline}}
.badge{{font-size:10px;text-transform:uppercase;letter-spacing:.04em;padding:1px 6px;border-radius:10px;margin-right:4px;vertical-align:1px}}
.badge.alta{{background:#dff5e3;color:#1a7f37}} .badge.media{{background:#fff0d6;color:#a36a00}} .badge.baja{{background:#eee;color:#888}}
.gold{{color:var(--gold);font-weight:600;text-decoration:line-through;text-decoration-color:#e9a8a4}}
.veda{{color:var(--veda);font-weight:600}}
.arr{{color:#999;padding:0 2px}} .ctx{{color:#9a9aa0}}
a{{color:#3357cc;text-decoration:none}} a:hover{{text-decoration:underline}}
.onlyalta .d:not(.t-alta){{display:none}}
.onlyalta .book:not(:has(.d.t-alta)){{display:none}}
.onlysel .d:not(.sel){{display:none}}
.onlysel .book:not(:has(.d.sel)){{display:none}}
</style></head><body>
<header>
<h1>Vedabase — Fase 2: cambios a revertir (escaneo = verdad)</h1>
<div class="meta"><b>{total}</b> candidatos · <b>{n_alta}</b> de confianza alta · marca = <b>seleccionar para revertir</b></div>
<div class="bar">
 <input id="q" type="search" placeholder="Buscar…">
 <button id="alta">Solo alta confianza</button>
 <span class="tag">Seleccionados: <span id="cnt">0</span></span>
 <button id="onlysel">Solo seleccionados</button>
 <button id="exp">Exportar seleccionados</button>
 <button id="clr">Limpiar</button>
</div>
</header>
<main>
<h3>Resumen por libro</h3>
<table><thead><tr><th>Libro</th><th>Título</th><th class="num">Candidatos</th><th class="num">Alta</th></tr></thead><tbody>{rows}</tbody></table>
{''.join(sections)}
</main>
<script>
const KEY='vedabase_phase2_selected';
let sel=JSON.parse(localStorage.getItem(KEY)||'{{}}');
const cnt=document.getElementById('cnt');
function refresh(){{cnt.textContent=Object.keys(sel).length;}}
function save(){{localStorage.setItem(KEY,JSON.stringify(sel));refresh();}}
function setSel(cb,on){{
 const id=cb.dataset.id,row=cb.closest('.d');
 cb.checked=on;
 if(on){{sel[id]={{book:cb.dataset.book,gold:cb.dataset.gold,veda:cb.dataset.veda}};row.classList.add('sel');}}
 else{{delete sel[id];row.classList.remove('sel');}}
}}
document.querySelectorAll('.sel').forEach(cb=>{{
 if(sel[cb.dataset.id]){{cb.checked=true;cb.closest('.d').classList.add('sel');}}
 cb.addEventListener('change',()=>{{setSel(cb,cb.checked);save();}});
}});
refresh();
// per-book mark all / unmark
document.querySelectorAll('.markall').forEach(b=>b.addEventListener('click',e=>{{e.preventDefault();
 document.querySelector('section[data-book="'+b.dataset.book+'"]').querySelectorAll('.sel').forEach(cb=>{{if(cb.closest('.d').style.display!=='none')setSel(cb,true);}});save();}}));
document.querySelectorAll('.unmarkall').forEach(b=>b.addEventListener('click',e=>{{e.preventDefault();
 document.querySelector('section[data-book="'+b.dataset.book+'"]').querySelectorAll('.sel').forEach(cb=>setSel(cb,false));save();}}));
// search
const q=document.getElementById('q');
q.addEventListener('input',()=>{{const t=q.value.toLowerCase();
 document.querySelectorAll('.book').forEach(b=>{{let any=false;
  b.querySelectorAll('.d').forEach(d=>{{const m=!t||d.textContent.toLowerCase().includes(t)||b.querySelector('h2').textContent.toLowerCase().includes(t);
   d.style.display=m?'':'none';if(m)any=true;}});
  b.style.display=any?'':'none';}});}});
// filters
const alta=document.getElementById('alta');
alta.addEventListener('click',()=>{{document.body.classList.toggle('onlyalta');alta.classList.toggle('on');}});
const onlysel=document.getElementById('onlysel');
onlysel.addEventListener('click',()=>{{document.body.classList.toggle('onlysel');onlysel.classList.toggle('on');}});
// export selected
document.getElementById('exp').addEventListener('click',()=>{{
 const items=Object.values(sel);
 if(!items.length){{alert('No has seleccionado nada.');return;}}
 items.sort((a,b)=>a.book.localeCompare(b.book));
 let txt='# Cambios a revertir en vedabase ('+items.length+')\\n\\nGOLD = original (correcto) · VEDA = vedabase (a corregir)\\n';
 let cur='';for(const it of items){{if(it.book!==cur){{cur=it.book;txt+='\\n## '+cur+'\\n';}}txt+='- en vedabase «'+it.veda+'»  ->  restaurar «'+it.gold+'»\\n';}}
 const a=document.createElement('a');a.href=URL.createObjectURL(new Blob([txt],{{type:'text/markdown'}}));a.download='vedabase_reversiones.md';a.click();
}});
document.getElementById('clr').addEventListener('click',()=>{{if(!confirm('¿Quitar todas las selecciones?'))return;
 sel={{}};localStorage.setItem(KEY,'{{}}');
 document.querySelectorAll('.sel').forEach(cb=>{{cb.checked=false;cb.closest('.d').classList.remove('sel');}});refresh();}});
</script>
</body></html>"""
    out = os.path.join(BASE, "reports", out_name)
    open(out, "w").write(doc)
    print(f"{total} candidatos ({n_alta} alta) · {len(books)} libros -> {out} ({len(doc)//1024} KB)")

if __name__ == "__main__":
    main()
