#!/usr/bin/env python3
"""
Rilegge dai file sorgente le righe di Valutazione perse dall'import di maggio
e le scrive in un JSON per app/fix-valutazione-recupero.mjs.

Due difetti distinti:
  A) elementi interi mai inseriti dai Word: l'import abbinava le righe al master
     per nome esatto e "Apparecchi di illuminazione" (nei file) non coincide con
     "Apparecchi d'illuminazione" (nel master). Qui il match è tollerante.
  B) P e G presenti negli Excel ma vuoti a database (22 righe su 12 sedi,
     sempre Rumore / Campi elettromagnetici / Impianti gas).

Legge i Word con row._tr.tc_lst, mai row.cells. Non tocca il DB.

  python3 import-tools/scripts/estrai_recupero_valutazione.py <copia.db> > /tmp/recupero_valutazione.json
"""
import sys, os, re, json, sqlite3, warnings, difflib
warnings.filterwarnings("ignore")
import openpyxl
from docx import Document

DVR_DIR = os.path.join(os.path.dirname(__file__), "..", "..", "-mat", "DVR")
db = sqlite3.connect(sys.argv[1])

def norm(s):
    s = re.sub(r"[ \t\r\n]+", " ", str(s or "")).strip()
    return s or None
# Chiave tollerante: via punteggiatura e maiuscole. Non basta da sola, perché
# nei file c'è "Apparecchi di illuminazione" dove il master ha "d'illuminazione":
# per quelle si passa dal confronto per somiglianza qui sotto.
def key(s): return re.sub(r"[^a-z0-9]", "", (s or "").lower())
def ip(v): return v if isinstance(v, int) and 0 <= v <= 4 else None
def ig(v): return v if isinstance(v, int) and 1 <= v <= 4 else None
def celltext(tc): return norm("".join(x.text for x in tc.iter() if x.tag.endswith("}t")))

elem = {key(nome): (eid, nome) for eid, nome in db.execute("SELECT id, nome FROM lookup_valutazione_elementi")}

def trova(nome):
    """Elemento del master corrispondente: prima esatto, poi per somiglianza
    (soglia alta, così 'di illuminazione' trova "d'illuminazione" ma due
    elementi diversi non si confondono)."""
    k = key(nome)
    if k in elem: return elem[k]
    vicini = difflib.get_close_matches(k, elem.keys(), n=1, cutoff=0.95)
    return elem[vicini[0]] if vicini else None
out = {"pg_vuoti": [], "elementi_mancanti": [], "incompleti": [], "scartati": []}

def righe_xlsx(path):
    ws = openpyxl.load_workbook(path, read_only=True, data_only=True)["Valutazione"]
    for r in ws.iter_rows(min_row=5, max_col=8, values_only=True):
        a = norm(r[0])
        if not a or re.fullmatch(r"\d+", a): continue
        yield a, norm(r[1]), norm(r[2]), norm(r[3]), ip(r[4]), ig(r[5])

def apri_docx(path):
    """Alcuni Word hanno un'immagine col CRC rotto (Torre Annunziata) e
    python-docx non li apre: se ne riscrive una copia senza quel media."""
    try:
        return Document(path)
    except Exception:
        import zipfile, tempfile
        fix = os.path.join(tempfile.gettempdir(), "riparato_" + os.path.basename(path))
        with zipfile.ZipFile(path) as zin, zipfile.ZipFile(fix, "w", zipfile.ZIP_DEFLATED) as zout:
            for it in zin.infolist():
                try: data = zin.read(it.filename)
                except Exception: data = b"\xff\xd8\xff\xd9"   # JPEG vuoto al posto di quello rotto
                zout.writestr(it, data)
        return Document(fix)

def righe_docx(path):
    for t in apri_docx(path).tables:
        for row in t.rows:
            c = [celltext(tc) for tc in row._tr.tc_lst]
            if not c or not c[0] or len(c) < 7: continue
            def i(x):
                try: return int(c[x]) if c[x] is not None else None
                except (TypeError, ValueError): return None
            yield c[0], c[1], c[2], c[3], ip(i(4)), ig(i(5))

for did, sf, fmt, aid in db.execute("SELECT d.id, d.source_file, d.source_format, d.agenzia_id FROM dvr d WHERE d.source_file IS NOT NULL"):
    path = os.path.join(DVR_DIR, sf)
    if not os.path.exists(path): continue
    try:
        righe = list(righe_xlsx(path) if fmt == "xlsx" else righe_docx(path))
    except Exception as e:
        out["scartati"].append({"file": sf, "motivo": str(e)[:80]}); continue
    presenti = {e: (p, g) for e, p, g in db.execute("SELECT elemento_id, p, g FROM dvr_valutazione WHERE dvr_id = ?", (did,))}
    visti = set()
    for nome, figure, rischi, misure, p, g in righe:
        k = trova(nome)
        if not k or k[0] in visti: continue
        eid, nome_master = k
        visti.add(eid)
        if eid not in presenti:
            # L'import l'aveva scartata: il nome nel file non coincideva col master.
            out["elementi_mancanti"].append({"dvr_id": did, "agenzia_id": aid, "file": sf, "elemento_id": eid,
                                             "elemento": nome_master, "nome_nel_file": nome, "figure_esposte": figure,
                                             "rischi_descr": rischi, "misure_prevenzione": misure, "p": p, "g": g})
        elif presenti[eid] == (None, None) and (p is not None or g is not None):
            # Serve la coppia completa: con un solo valore il livello di rischio non
            # si calcola. Le righe mezze piene (nel file la Gravità è 0, fuori scala)
            # restano fuori e si elencano a parte.
            dest = "pg_vuoti" if (p is not None and g is not None) else "incompleti"
            out[dest].append({"dvr_id": did, "agenzia_id": aid, "file": sf, "elemento_id": eid,
                              "elemento": nome_master, "p": p, "g": g})

json.dump(out, sys.stdout, ensure_ascii=False, indent=1)
sys.stderr.write(f"pg_vuoti={len(out['pg_vuoti'])} elementi_mancanti={len(out['elementi_mancanti'])} "
                 f"incompleti={len(out['incompleti'])} file_illeggibili={len(out['scartati'])}\n")
