#!/usr/bin/env python3
"""Paridad repo/D1 de TODO el corpus, un libro por consulta.

`verificar_paridad.py` va en lotes de 40 referencias, una consulta por lote:
para los 20.000 versos son 500 idas y venidas. D1 devuelve un libro entero en
una sola consulta —11.359 filas del Caitanya-caritamrta sin despeinarse—, asi
que aqui se trae el libro de golpe y se compara en memoria.

Compara traduccion + significado + synonyms. El campo synonyms lo añadi el
15-ago, cuando salieron 62 divergencias que ningun verificador miraba.

La comparacion es por PALABRAS, no por cadena: el repo lleva marcado de cursiva
y D1 no siempre, y una diferencia de espacios no es una divergencia.

Uso:
    python3 paridad_completa.py                    # ingles, todos los libros
    python3 paridad_completa.py --libro cc
    python3 paridad_completa.py --lang es          # espanol (translations/espanol)
"""
import collections
import difflib
import json
import os
import pathlib
import re
import subprocess
import sys

HERE = pathlib.Path(__file__).resolve().parent
ASTRO = HERE.parent.parent
REPO = pathlib.Path('/Users/jaganat/git_projects/vedabase-original')
sys.path.insert(0, str(HERE))
from verificar_paridad import ref_de  # noqa: E402

CARPETA = {'bg': 'bhagavad-gita-as-it-is', 'sb': 'srimad-bhagavatam',
           'cc': 'sri-caitanya-caritamrta', 'iso': 'isopanisad',
           'kb': 'krsna-the-supreme-personality-of-godhead',
           'tlc': 'teachings-of-lord-caitanya', 'nod': 'nectar-of-devotion',
           'ssr': 'science-of-self-realization', 'tqk': 'teachings-of-queen-kunti',
           'noi': 'nectar-of-instruction', 'lcfl': 'life-comes-from-life',
           'ttp': 'topmost-yoga-system', 'ej': 'easy-journey-to-other-planets',
           'krp': 'reservoir-of-pleasure', 'bbd': 'beyond-birth-and-death',
           'ekc': 'elevation-to-krsna-consciousness', 'lob': 'light-of-the-bhagavata',
           'owk': 'on-the-way-to-krsna', 'pop': 'path-of-perfection',
           'poy': 'perfection-of-yoga', 'pqpa': 'perfect-questions-perfect-answers',
           'rv': 'raja-vidya'}


def d1(sql):
    p = subprocess.run(['npx', 'wrangler', 'd1', 'execute', 'vedabase-search-db', '--remote',
                        '--json', '--command', sql], capture_output=True, text=True,
                       cwd=str(ASTRO),
                       env={**os.environ, 'CF_API_TOKEN': '', 'CLOUDFLARE_API_TOKEN': ''})
    try:
        return json.loads(p.stdout)[0]['results']
    except Exception:
        sys.exit(f'D1 no responde:\n{p.stderr[-500:]}')


INDIO = re.compile(r'[\u0900-\u097F\u0980-\u09FF]')


def pal(s):
    """Palabras comparables, con los mismos descartes que `verificar_paridad`.

    Solo se quitan los encabezados. `verificar_paridad` descarta ademas las
    lineas de cita y las indias porque compara contra translation+purport, donde
    no tienen contraparte; aqui se traen TODOS los campos de D1 —verso,
    devanagari, glosa, traduccion y significado— asi que descartarlas volveria
    la comparacion asimetrica. Con el descarte, `cc/adi/3/summary` salia con 427
    palabras «que faltan en D1» y estaban ahi, en `verse_text`.
    """
    lineas = (s or '').splitlines()
    fuera = []
    for i, l in enumerate(lineas):
        if l.startswith('#'):
            # El PRIMER encabezado es la cabecera que pone el exportador
            # («### 11 11. Aspects of…»): duplica el titulo y no esta en D1.
            # Los demas SI estan, dentro del significado («## Servitorship»),
            # y quitarlos daba 35 falsos positivos solo en el Nectar of Devotion.
            if not fuera:
                continue
            fuera.append(re.sub(r'^#+\s*', '', l))
            continue
        fuera.append(re.sub(r'^\s*>\s?', '', l))
    t = re.sub(r'</?(em|i|strong|b)>', '', '\n'.join(fuera))
    t = t.replace('*', '').replace('_', '').replace('\\', ' ')
    return re.sub(r'[^\w\sĀ-ỿ]', ' ', t).split()


IDIOMA_DIR = {'es': 'espanol', 'pt': 'portugues', 'hi': 'hindi', 'ru': 'russian'}


def raiz_de(lang):
    """Donde vive el texto de ese idioma en el repo.

    El ingles esta en la raiz (`bhagavad-gita-as-it-is/...`) y los demas bajo
    `translations/<idioma>/`, con la MISMA estructura de carpetas debajo. El
    arabe no esta en ningun sitio: existe solo en D1.
    """
    if lang == 'en':
        return REPO
    d = IDIOMA_DIR.get(lang)
    if not d:
        sys.exit(f'{lang}: no hay texto de ese idioma en el repo')
    return REPO / 'translations' / d


def main():
    solo = sys.argv[sys.argv.index('--libro') + 1] if '--libro' in sys.argv else None
    lang = sys.argv[sys.argv.index('--lang') + 1] if '--lang' in sys.argv else 'en'
    raiz = raiz_de(lang)
    libros = [solo] if solo else list(CARPETA)
    salida = []
    for libro in libros:
        carp = CARPETA[libro]
        if not (raiz / carp).is_dir():
            continue
        ficheros = {}
        for f in (raiz / carp).rglob('*.md'):
            r = ref_de(str(f.relative_to(raiz)))
            if r:
                ficheros[r] = f
        filas = {x['ref']: x for x in d1(
            f"SELECT ref, COALESCE(translation,'') t, COALESCE(purport,'') p, "
            f"COALESCE(synonyms,'') s, COALESCE(verse_text,'') v, "
            f"COALESCE(devanagari,'') d FROM verses "
            f"WHERE lang='{lang}' AND book='{libro}'")}
        malos = sinfila = 0
        for ref, f in sorted(ficheros.items()):
            fila = filas.get(ref)
            if fila is None:
                sinfila += 1
                continue
            texto = f.read_text(encoding='utf-8')
            # Dos filas de D1 juntan lo que el repo guarda en dos ficheros:
            #   sb/0b = introduction.md + a-short-sketch-of-the-life-and-teachings.md
            #   kb/0d lleva ademas «Introduction» como titulo, que en el repo es
            #         el nombre del fichero y no aparece en el texto
            # Sin esto, sb/0b salia con 15.810 palabras «solo en D1» y tapaba las
            # 25 divergencias de verdad que habia dentro de la introduccion.
            if ref == 'sb/0b':
                otro = f.parent / 'a-short-sketch-of-the-life-and-teachings.md'
                if otro.exists():
                    texto += '\n' + otro.read_text(encoding='utf-8')
            # El titulo «Introduction» de kb/0d es el nombre del fichero en el
            # repo ingles y no aparece en su texto. En los idiomas traducidos el
            # titulo es otro y ya viene dentro del .md, asi que anteponer la
            # palabra inglesa daba una divergencia falsa en los cuatro.
            if ref == 'kb/0d' and lang == 'en':
                texto = 'Introduction\n' + texto
            # El colofon de capitulo («Thus end the Bhaktivedanta Purports…») vive
            # en su propio fichero en el repo —bg-N-colophon.md— y D1 lo pega al
            # ultimo verso. Comprobado en los capitulos 1, 2, 9 y 18: el texto es
            # el mismo. Es estructura, no divergencia, y sin esto salian los 18
            # capitulos de la Gita como divergentes.
            # Solo al verso al que D1 se lo pega —el ultimo del capitulo—, no a
            # todos: pegarselo a los 653 versos de la Gita los volvia divergentes.
            if 'Thus end the Bhaktivedanta' in fila['p']:
                col = next(f.parent.glob('*-colophon.md'), None)
                if col:
                    texto += '\n' + col.read_text(encoding='utf-8')
            a = pal(texto)
            b = pal(' '.join((fila['v'], fila['d'], fila['s'], fila['t'], fila['p'])))
            if collections.Counter(a) == collections.Counter(b):
                continue
            # Por DIFERENCIA DE MULTICONJUNTOS, no por diff de secuencia. El .md
            # ordena verso, glosa, traduccion y significado; D1 son cuatro campos
            # que aqui se concatenan en otro orden, asi que un diff posicional
            # marca como divergencia lo que solo es un cambio de sitio: los 10
            # primeros «falta en D1» del 15-ago eran eso y nada mas.
            ca, cb = collections.Counter(a), collections.Counter(b)
            solo_repo = sorted((ca - cb).elements())
            solo_d1 = sorted((cb - ca).elements())
            if not solo_repo and not solo_d1:
                continue
            malos += 1
            salida.append({'ref': ref, 'lang': lang, 'file': str(f.relative_to(raiz)),
                           'solo_repo': solo_repo[:40], 'solo_d1': solo_d1[:40],
                           'n_repo': len(solo_repo), 'n_d1': len(solo_d1)})
        print(f'  {libro:4} repo={len(ficheros):5} D1={len(filas):5}  '
              f'divergentes={malos:4}  sin fila en D1={sinfila}')
    nombre = 'paridad_completa.json' if lang == 'en' else f'paridad_completa_{lang}.json'
    (HERE / nombre).write_text(
        json.dumps(salida, ensure_ascii=False, indent=1), encoding='utf-8')
    print(f'\n  total divergentes: {len(salida)}   -> {nombre}')
    return 0


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