#!/usr/bin/env python3
"""Cierra en el ledger los lotes 140-199 (la cola de candidatos abiertos).

El detalle que hay que acertar es la CLAVE. El ledger indexa por
`(norm(repo), norm(scan))` con los valores tal como los dejo el alineador — es
decir, con el titulillo de la pagina todavia pegado al campo `scan`. Los lotes,
en cambio, se construyeron con el `scan` ya limpio para que el adjudicador
pudiera leerlo.

Si el veredicto se escribe con el texto limpio, no cierra nada: es el mismo
fallo que dejo 216 correcciones sin registrar esta manana. Asi que aqui se
recupera el `scan` ORIGINAL desde `abiertos_limpios.json`, que guarda los dos.

Veredictos que se traducen:
  IMPRESO=REPO / ESTA          -> RUIDO       el impreso da la razon al repo
  TRANSCRITO                   -> pendiente   hay que escribir la forma impresa
  NO_ESTA                      -> pendiente   hay que suprimir
  DISTINTO                     -> ABIERTO     tercera lectura, a mano
  NO_LEGIBLE / NO_ENCONTRADO   -> DESCARTADO  sin prueba no se toca el texto

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

HERE = pathlib.Path(__file__).resolve().parent
LOTES = HERE / 'lotes_imagen'
FECHA = '2026-08-11'

A_RUIDO = {'IMPRESO=REPO', 'ESTA', 'IGUAL'}   # 'IGUAL' lo usan los lotes 170+
A_DESCARTE = {'NO_LEGIBLE', 'NO_ENCONTRADO'}
PENDIENTE = {'TRANSCRITO', 'NO_ESTA', 'DISTINTO', 'RESTAURAR'}


def main():
    aplicar = '--aplicar' in sys.argv

    # el candidato original, con el `scan` sucio que es lo que indexa el ledger
    orig = {}
    p = HERE / 'abiertos_limpios.json'
    if p.exists():
        for x in json.load(open(p, encoding='utf-8')):
            orig[(x['file'], (x.get('repo') or '').strip(), x.get('scan_limpio', ''))] = x
    # SEGUNDA FUENTE: los propios ficheros de lote. Los lotes 170+ se armaron
    # directamente desde el ledger, no desde `abiertos_limpios.json`, asi que
    # ahi no estan y la clave original se perdia: 103 casos adjudicados cerraban
    # solo 2. El lote guarda `texto_repo` y `scan` tal como los dejo el
    # alineador, que es justo lo que el ledger indexa.
    porcaso = {}
    for lf in sorted(LOTES.glob('lote1[0-9][0-9].json')):
        try: datos = json.load(open(lf, encoding='utf-8'))
        except Exception: continue
        for x in datos:
            if not isinstance(x, dict) or 'file' not in x: continue
            k = (x['file'], (x.get('texto_repo') or x.get('repo') or '').strip())
            porcaso.setdefault(k, {'repo': x.get('texto_repo') or x.get('repo') or '',
                                   'scan': x.get('scan', '')})

    filas = []
    for f in sorted(LOTES.glob('resultado*.json')):
        n = int(re.search(r'(\d+)', f.name).group(1))
        if not (140 <= n <= 199): continue
        filas += json.load(open(f, encoding='utf-8'))
    if not filas:
        print('todavia no hay resultados de estos lotes'); return 1

    res = collections.Counter(x.get('veredicto') for x in filas)
    print(f'casos adjudicados: {len(filas)}')
    for k, v in res.most_common(): print(f'  {v:4}  {k}')

    lineas = []; pendientes = []; sinorigen = 0
    for x in filas:
        v = x.get('veredicto')
        clave = (x.get('file', ''), (x.get('repo') or x.get('texto_repo') or '').strip(),
                 x.get('scan', ''))
        o = orig.get(clave)
        if not o:
            # se prueba sin el scan, por si el adjudicador no lo devolvio igual
            cand = [y for k, y in orig.items() if k[0] == clave[0] and k[1] == clave[1]]
            o = cand[0] if len(cand) == 1 else None
        if not o:
            o = porcaso.get((x.get('file', ''),
                             (x.get('texto_repo') or x.get('repo') or '').strip()))
        if not o: sinorigen += 1
        repo_k = (o or {}).get('repo') or x.get('repo') or x.get('texto_repo') or ''
        scan_k = (o or {}).get('scan', '') if o else x.get('scan', '')

        if v in A_RUIDO:
            lineas.append({"repo": repo_k, "scan": scan_k, "estado": "RUIDO",
                           "razon": f"adjudicado sobre la IMAGEN del impreso: coincide con el repo. "
                                    f"{(x.get('nota') or '')[:170]}", "fecha": FECHA})
        elif v in A_DESCARTE:
            lineas.append({"repo": repo_k, "scan": scan_k, "estado": "DESCARTADO",
                           "razon": f"{v} sobre la imagen: sin ver el pixel no se cambia el texto. "
                                    f"{(x.get('nota') or '')[:170]}", "fecha": FECHA})
        elif v in PENDIENTE:
            pendientes.append({**x, 'repo_original': repo_k, 'scan_original': scan_k})
        else:
            lineas.append({"repo": repo_k, "scan": scan_k, "estado": "ABIERTO",
                           "razon": f"veredicto inesperado ({v}): revisar a mano", "fecha": FECHA})

    print(f'\ncierres directos: {len(lineas)} | pendientes de aplicar: {len(pendientes)}')
    if sinorigen: print(f'sin candidato original identificado: {sinorigen}')
    json.dump(pendientes, open(HERE / 'abiertos_pendientes.json', 'w'), ensure_ascii=False, indent=1)
    for x in pendientes[:12]:
        print(f"  {x['veredicto']:12} {x['file'].split('/')[-1]:22} "
              f"{str(x.get('palabra_repo') or x.get('texto_repo'))[:26]!r} -> "
              f"{str(x.get('impreso') or '(suprimir)')[:34]!r}")
    # Los PENDIENTES tambien necesitan veredicto con la clave del ledger. Los
    # aplicadores escriben el suyo con el `scan` TRANSCRITO ('purusahs'), no con
    # el original sucio ('purusahs or the sky beyond the universe 49'), asi que
    # el candidato seguia saliendo ABIERTO por mucho que estuviera resuelto.
    # Aqui se comprueba en el fichero si el cambio ya esta hecho y se cierra con
    # la clave que el ledger sabe leer.
    import unicodedata
    def sd(x):
        x = unicodedata.normalize('NFD', x or '')
        x = ''.join(c for c in x if unicodedata.category(c) != 'Mn')
        return ' '.join(re.sub(r'[^A-Za-z0-9]+', ' ', x.lower()).split())
    REPO = pathlib.Path('/Users/jaganat/git_projects/vedabase-original')
    cache = {}; cerrados = 0
    for x in pendientes:
        f = x.get('file')
        if not f or not (REPO / f).exists(): continue
        if f not in cache: cache[f] = sd((REPO / f).read_text(encoding='utf-8'))
        viejo = sd(x.get('frase_repo') or x.get('palabra_repo') or x.get('texto_repo')
                   or x.get('repo_original') or '')
        nuevo = sd(x.get('frase_impreso') or x.get('impreso') or '')
        if not viejo: continue
        if viejo not in cache[f] or (nuevo and nuevo in cache[f]):
            lineas.append({"repo": x.get('repo_original', ''), "scan": x.get('scan_original', ''),
                           "estado": "APLICADO",
                           "razon": f"adjudicado por imagen y ya escrito en el fichero: "
                                    f"{(x.get('nota') or '')[:170]}", "fecha": FECHA})
            cerrados += 1
    if cerrados: print(f'pendientes que ya estaban aplicados y se cierran: {cerrados}')

    if aplicar:
        with open(HERE / 'verdicts.jsonl', 'a') as fh:
            for l in lineas: fh.write(json.dumps(l, ensure_ascii=False) + '\n')
        print(f'\nveredictos escritos: {len(lineas)}')
    else:
        print('\n(ensayo: anade --aplicar)')
    return 0


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