#!/usr/bin/env python3
# Capa 3 (opcional): review semántica con Gemma LOCAL, SOLO sobre páginas marcadas por la Capa 1.
# Coste 0 (LLM local). Si Gemma no responde, sale limpio y deja constancia.
import json, os, urllib.request
DIR=os.path.dirname(os.path.abspath(__file__))
GEMMA="http://172.19.128.1:1234/v1/chat/completions"; MODEL="google/gemma-4-e4b"
MAX=int(os.environ.get('MAX','40'))
LNAME={'en':'inglés','fr':'francés','it':'italiano','pt':'portugués','es':'español'}

# texto visible está en report.jsonl (no en report.json)
texts={}
for line in open(os.path.join(DIR,'report.jsonl'),encoding='utf-8'):
    line=line.strip()
    if not line: continue
    r=json.loads(line); texts[(r['lang'],r['url'])]=r.get('text','')

rep=json.load(open(os.path.join(DIR,'report.json'),encoding='utf-8'))
flagged=[r for r in rep if any(f in r.get('flags',[]) for f in ('SPANISH_LEAK','LANG_MISMATCH'))][:MAX]
print(f"páginas marcadas para review LLM: {len(flagged)} (cap {MAX})")

def ask(lang,text):
    prompt=(f"Eres revisor de QA de un sitio web. Esta página DEBERÍA estar en {LNAME.get(lang,lang)}. "
        f"Evalúa el texto visible. Responde SOLO un JSON válido: "
        f'{{"idioma_ok": true/false, "idioma_predominante": "es/en/fr/it/pt", "problemas": ["lista breve de mistraducciones, trozos sin traducir o sin sentido"]}}.\n\nTEXTO:\n'+text[:1500])
    body=json.dumps({"model":MODEL,"messages":[{"role":"user","content":prompt}],"temperature":0.1,"max_tokens":2200}).encode()
    req=urllib.request.Request(GEMMA,data=body,headers={"Content-Type":"application/json"})
    r=json.load(urllib.request.urlopen(req,timeout=120))
    out=r["choices"][0]["message"]["content"].strip()
    s=out.find('{'); e=out.rfind('}')
    return json.loads(out[s:e+1]) if s>=0 else {"raw":out[:200]}

results=[]
try:
    urllib.request.urlopen("http://172.19.128.1:1234/v1/models",timeout=6)
except Exception as e:
    print("Gemma no responde, salto Capa 3:",e); 
    json.dump({"skipped":True,"reason":str(e)},open(os.path.join(DIR,'llm_review.json'),'w')); raise SystemExit(0)

for i,r in enumerate(flagged):
    t=texts.get((r['lang'],r['url']),'')
    if len(t)<60: continue
    try:
        v=ask(r['lang'],t); v.update({'url':r['url'],'lang':r['lang'],'flags':r['flags']}); results.append(v)
        print(f"  [{i+1}/{len(flagged)}] {r['lang']} ok={v.get('idioma_ok')} pred={v.get('idioma_predominante')}")
    except Exception as e:
        print(f"  [{i+1}] error {e}")
json.dump(results,open(os.path.join(DIR,'llm_review.json'),'w',encoding='utf-8'),ensure_ascii=False,indent=1)
print(f"review LLM guardada: {len(results)} páginas")
