#!/usr/bin/env python3
"""Per-canto / per-lila scan-vs-D1 localizer for sb and cc.
Scan is split into volumes; D1 is one book. Align each canto/lila group:
  scan = concat of that group's volumes ; D1 = rows with ref prefix.
Same safety as scan_vs_d1_apply: garbage filter, size guard, EXACT cross-check
vs approved list, hard offset gate, exact substr edits.
"""
import json, re, difflib, sys, unicodedata
import scan_vs_d1_apply as A
fold=A.fold; foldsp=A.foldsp; garbage=A.garbage; clean_md=A.clean_md; natkey=A.natkey

COLS=('verse_text','synonyms','translation','purport')

def groups():
    src=open(A.os.path.join(A.BASE,'compare.py')).read()
    keys=re.findall(r'"(sb\d+|cc_\w+)":\s*\{',src)
    g={}
    for k in keys:
        if k.startswith('sb'):
            canto=k[2:-1]; g.setdefault(('sb','sb/'+canto+'/'),[]).append(k)
        else:
            lila={'adi':'cc/adi/','mad':'cc/madhya','ant':'cc/antya'}[k.split('_')[1][:3]]
            g.setdefault(('cc',lila),[]).append(k)
    return g

def approved_set(book):
    """union of all approved (veda_folded, gold_folded) for sb*/cc_* sections."""
    cur=None; out=set()
    pref = 'sb' if book=='sb' else 'cc'
    for line in open(A.os.path.join(A.BASE,'correcciones_LIMPIAS.md'),encoding='utf-8'):
        h=re.match(r'^#{2,3}\s+([A-Za-z_0-9]+)',line)
        if h: cur=h.group(1)
        m=re.search(r'buscar «(.+?)»\s*→\s*reemplazar por «(.+?)»',line)
        if not m: continue
        tag=re.search(r'\[([a-z_0-9]+)',line)
        sec=tag.group(1) if tag else cur
        if sec and sec.startswith(pref):
            out.add((foldsp(m.group(1)),foldsp(m.group(2))))
    return out

def scan_words_multi(keys):
    w=[]
    for k in sorted(keys, key=natkey):
        w+=A.scan_words(k)
    return w

def d1_tokens_pref(rows, ref_pref):
    toks=[]
    sub=[r for r in rows if r['ref'].startswith(ref_pref)]
    for r in sorted(sub, key=lambda r:natkey(r['ref'])):
        for col in COLS:
            raw=r.get(col) or ''
            for m in re.finditer(r'\S+', raw):
                wc=clean_md(m.group(0))
                if wc.strip(): toks.append((wc, r['id'], col, m.start(), m.end(), r['ref']))
    return toks

def run_group(book, ref_pref, scan_keys, appr, rows, cellmap):
    sw=scan_words_multi(scan_keys)
    dt=d1_tokens_pref(rows, ref_pref)
    if not dt: return [],0
    sm=difflib.SequenceMatcher(None,[fold(w) for w in sw],[fold(t[0]) for t in dt],autojunk=False)
    edits=[]; raw=0
    for tag,i1,i2,j1,j2 in sm.get_opcodes():
        if tag=='equal': continue
        raw+=1
        if max(i2-i1,j2-j1)>8: continue
        gold=' '.join(sw[i1:i2]).strip(); veda=' '.join(t[0] for t in dt[j1:j2]).strip()
        if garbage(gold.split()): continue
        fg,fv=foldsp(gold),foldsp(veda)
        if (fv,fg) not in appr: continue
        ids={t[1] for t in dt[j1:j2]}; cols={t[2] for t in dt[j1:j2]}
        if len(ids)!=1 or len(cols)!=1 or j2==j1: continue
        cid=dt[j1][1]; col=dt[j1][2]; cs=dt[j1][3]; ce=dt[j2-1][4]; ref=dt[j1][5]
        if foldsp(cellmap.get((cid,col),'')[cs:ce])!=fv: continue
        edits.append({'id':cid,'col':col,'ref':ref,'cs':cs,'ce':ce,'veda':veda,'gold':gold})
    return edits, raw

if __name__=='__main__':
    book=sys.argv[1]; only=sys.argv[2] if len(sys.argv)>2 else None
    d1=json.load(open('/tmp/d1_en.json'))[0]['results']
    rows=[r for r in d1 if r['book']==book]
    cellmap={(r['id'],c):(r.get(c) or '') for r in rows for c in COLS}
    appr=approved_set(book)
    g=groups()
    total=[]
    for (bk,ref_pref),keys in sorted(g.items()):
        if bk!=book: continue
        if only and only not in ref_pref: continue
        edits,raw=run_group(book,ref_pref,keys,appr,rows,cellmap)
        print(f'{ref_pref:14} vols={len(keys)} raw={raw:6} -> edits={len(edits)}')
        total+=edits
    print(f'TOTAL {book} edits: {len(total)} (approved set={len(appr)})')
    json.dump(total, open(f'/tmp/{book}_edits.json','w'))
    for e in total[:12]:
        print(f"  [{e['ref']}/{e['col']}] «{e['veda'][:34]}» → «{e['gold'][:34]}»")
