#!/usr/bin/env python3
"""Deshace en el repo los cambios que la ADJUDICACION POR IMAGEN desmiente.

Los ocho lotes de `lotes_imagen/resultadoN.json` traen, por cada cambio que
`segunda_opinion.py` aplico, el veredicto de haber mirado la pagina escaneada:

  IMPRESO=SCAN   la imagen da la razon al cambio -> se queda, y se anota
                 VERIFICADO para que ninguna pasada futura lo vuelva a proponer.
  IMPRESO=REPO   la imagen desmiente el cambio -> se deshace.
  IMPRESO=OTRO   la imagen trae una tercera lectura -> se deshace y se anota.
  NO_LEGIBLE / NO_ENCONTRADO -> se deshace: sin prueba no se toca el texto.

COMO se deshace, y por que asi. La primera version de este script revertia con
una sustitucion scan->repo en todo el fichero, y eso es EXACTAMENTE el fallo que
obligo a revertir el pase masivo: deshacer 'satisfied'->'ever unsatisfied' en
sb-3.28.16 habria tocado todos los 'satisfied' del significado. Siete casos
daban doce sustituciones. Aqui no se sustituye nada: el fichero se restaura
desde git (HEAD ya esta en el estado bueno, los cambios de segunda_opinion estan
sin commitear) y se le vuelven a aplicar SOLO los pares que la imagen confirmo,
con la misma garantia de recuento.

POR QUE HACE FALTA REVISAR. La capa de texto del PDF es un segundo motor de OCR
y acierta, pero confirma lo que dice ESA PAGINA, no lo que el alineador creia
senalar. Cuando el alineador atribuye un candidato a la pagina equivocada, los
dos motores coinciden sobre el texto que no es: en el TLC cap.3 la capa hallo
'Teachings' y era el TITULILLO; en SB 4.7.26 el cambio dejo «Even though You
appear in the material energy, But. You are always free», que no es texto; en
SB 3.23.17 cruzo la traduccion con el significado de la misma pagina. Solo la
imagen distingue esos casos.

Uso: python3 deshacer_imagen.py [--aplicar]
"""
import json, re, sys, subprocess, pathlib, collections

HERE = pathlib.Path(__file__).resolve().parent
REPO = pathlib.Path('/Users/jaganat/git_projects/vedabase-original')
LOTES = HERE / 'lotes_imagen'
FECHA = '2026-08-09'


def sub(txt, a, b):
    """Sustituye con limite de palabra y respetando la mayuscula inicial. El
    limite es obligatorio en las TRES copias (repo, D1, content/en): mezclar
    \\b con un REPLACE por subcadena ya corrompio texto ('nothing'->'nnothing')."""
    def f(m):
        return b.capitalize() if m.group(0)[:1].isupper() else b
    return re.subn(rf'\b{re.escape(a)}\b', f, txt, flags=re.I)


def main():
    aplicar = '--aplicar' in sys.argv
    filas = []
    for f in sorted(LOTES.glob('resultado*.json')):
        filas += json.load(open(f, encoding='utf-8'))
    if not filas:
        print('no hay resultados de imagen todavia'); return 1

    res = collections.Counter(x['veredicto'] for x in filas)
    for k, v in res.most_common(): print(f'  {v:4}  {k}')

    revocados = {(x['file'], x['repo'].lower(), x['scan'].lower())
                 for x in filas if x['veredicto'] != 'IMPRESO=SCAN'}
    afectados = sorted({f for f, _, _ in revocados})

    # cuantos casos senalo el alineador para cada (fichero, par): la garantia
    aplicados = json.load(open(HERE / 'ultimas_grafias_segunda.json', encoding='utf-8'))
    casos = collections.Counter()
    for cf in sorted((HERE / 'out_surya').glob('*.candidates.jsonl')):
        for l in open(cf, encoding='utf-8'):
            r = json.loads(l)
            if r['file'] not in afectados: continue
            casos[(r['file'], (r.get('repo') or '').strip().lower(),
                   (r.get('scan') or '').strip().lower())] += 1

    verd = []
    for x in filas:
        if x['veredicto'] == 'IMPRESO=SCAN':
            verd.append((x['repo'], x['scan'], 'VERIFICADO',
                         f"adjudicado por IMAGEN del impreso (p.{x['pagina']}): {x['nota'][:160]}"))
        else:
            raz = (f"la IMAGEN del impreso (p.{x['pagina']}) desmiente el cambio: {x['nota'][:200]}"
                   if x['veredicto'] == 'IMPRESO=REPO' else
                   f"{x['veredicto']} sobre la imagen (p.{x['pagina']}). {x['nota'][:200]}")
            estado = 'DESCARTADO' if x['veredicto'] == 'IMPRESO=REPO' else 'ABIERTO'
            verd.append((x['repo'], x['scan'], estado, raz))

    print(f'\nficheros a restaurar: {len(afectados)} | pares revocados: {len(revocados)}')
    rehechos = 0
    for fich in afectados:
        quedan = [(a, b) for a, b in aplicados.get(fich, [])
                  if (fich, a, b) not in revocados]
        print(f'  {fich}: se restaura y se le devuelven {len(quedan)} par(es) confirmado(s)')
        if not aplicar: continue
        subprocess.run(['git', 'checkout', 'HEAD', '--', fich], cwd=str(REPO), check=True)
        p = REPO / fich
        t = orig = p.read_text(encoding='utf-8')
        for a, b in quedan:
            n = len(re.findall(rf'\b{re.escape(a)}\b', t, flags=re.I))
            if n != casos.get((fich, a, b), 0):
                print(f'    apartado {a!r}->{b!r}: {n} ocurrencias, {casos.get((fich, a, b), 0)} senaladas')
                continue
            t, c = sub(t, a, b); rehechos += c
        if t != orig: p.write_text(t, encoding='utf-8')
        aplicados[fich] = quedan

    if aplicar:
        print(f'pares confirmados re-aplicados: {rehechos}')
        json.dump(aplicados, open(HERE / 'ultimas_grafias_segunda.json', 'w'),
                  ensure_ascii=False, indent=1)
        json.dump(sorted(revocados), open(HERE / 'revocados_imagen.json', 'w'),
                  ensure_ascii=False, indent=1)
        with open(HERE / 'verdicts.jsonl', 'a') as fh:
            for a, b, est, raz in verd:
                fh.write(json.dumps({"repo": a, "scan": b, "estado": est,
                                     "razon": raz, "fecha": FECHA}, ensure_ascii=False) + '\n')
        print('veredictos escritos:', len(verd))
    return 0


if __name__ == '__main__':
    sys.exit(main())
