#!/usr/bin/env python3
"""
Estrae dal master TWISTER v2 le tendine del foglio Valutazione.

Le voci delle tendine NON stanno nella cella: stanno nel foglio ListaF2 e la
cella le richiama con una data validation in formato x14, che openpyxl non
legge (`ws.data_validations` risulta vuoto). Vanno quindi prese dall'XML
grezzo di xl/worksheets/sheet5.xml.

Nel range richiamato la prima cella è l'elenco aggregato (titolo + "1- … 2- …"),
cioè lo stesso testo che si vede nella cella del foglio; le celle successive
sono le singole voci selezionabili. L'import originale aveva ricavato le voci
spezzando il testo aggregato, e dove l'aggregato non era numerato (Impianti
gas, Esplosioni, Legionellosi, Posti di lavoro) si era fermato alla prima.

Output: JSON con, per ogni elemento, i gruppi di scelta nell'ordine in cui
compaiono nel foglio.

    python3 estrai_valutazione_master.py /tmp/valutazione_master.json
"""
import json
import re
import sys
import warnings
import zipfile
from html import unescape
from pathlib import Path

import openpyxl

warnings.filterwarnings("ignore")

MASTER = Path(__file__).resolve().parents[2] / "-mat" / "07_DVR AGENZIE_TWISTER_v2.xlsx"
PUNTEGGIO = re.compile(r"\(\s*(\d)\s*-\s*(\d)\s*-\s*(\d)\s*\)")
NUMERO_INIZIALE = re.compile(r"^\s*\d+\s*[-–_.)]\s*")


def norm(v):
    if v is None:
        return ""
    return re.sub(r"[ \t]+", " ", str(v)).strip()


def spazi(v):
    """Chiave di confronto: niente numerazione, niente punteggi, minuscolo."""
    t = PUNTEGGIO.sub(" ", norm(v).replace("\n", " "))
    t = NUMERO_INIZIALE.sub("", t)
    return re.sub(r"\s+", " ", t).strip(" .").lower()


def aggregato(v):
    """Vero se la cella elenca già le voci numerate ("1- …", "2- …")."""
    return sum(1 for riga in str(v).splitlines() if NUMERO_INIZIALE.match(riga)) >= 2


def validazioni(path, sheet_xml):
    """{'H30': ('ListaF2', 'B', 23, 27), …} dalle x14:dataValidation."""
    xml = zipfile.ZipFile(path).read(sheet_xml).decode("utf8")
    out = {}
    for blocco in re.findall(r"<x14:dataValidation\b.*?</x14:dataValidation>", xml, re.S):
        f = re.search(r"<xm:f>(.*?)</xm:f>", blocco, re.S)
        sq = re.search(r"<xm:sqref>(.*?)</xm:sqref>", blocco, re.S)
        if not f or not sq:
            continue
        m = re.match(r"^([^!]+)!\$([A-Z]+)\$(\d+):\$[A-Z]+\$(\d+)$", unescape(f.group(1)).strip())
        if not m:
            continue
        foglio, col, r1, r2 = m.group(1), m.group(2), int(m.group(3)), int(m.group(4))
        for ref in unescape(sq.group(1)).split():
            if ":" in ref:  # range di celle: qui non ci interessa
                continue
            out[ref] = (foglio, col, r1, r2)
    return out


def main(dest):
    wb = openpyxl.load_workbook(MASTER)
    ws = wb["Valutazione"]
    dv = validazioni(MASTER, "xl/worksheets/sheet5.xml")

    elementi = []
    corrente = None
    for r in range(5, ws.max_row + 1):
        nome = norm(ws.cell(r, 1).value)
        if nome and not nome.isdigit():
            corrente = {"elemento": nome, "chiave": spazi(nome), "D": [], "H": []}
            elementi.append(corrente)
        elif nome.isdigit():
            corrente = None  # riga di capitolo
        if corrente is None:
            continue
        for col, idx in (("D", 4), ("H", 8)):
            testo = norm(ws.cell(r, idx).value)
            if not testo:
                continue
            gruppo = {"cella": f"{col}{r}", "testo": testo, "voci": []}
            rif = dv.get(f"{col}{r}")
            if rif:
                foglio, lcol, r1, r2 = rif
                lista = wb[foglio]
                for rr in range(r1, r2 + 1):
                    v = norm(lista[f"{lcol}{rr}"].value)
                    if not v:
                        continue
                    if spazi(v) == spazi(testo) or aggregato(v):
                        continue  # è l'elenco aggregato, non una voce
                    gruppo["voci"].append(v)
            corrente[col].append(gruppo)

    for e in elementi:
        for col in ("D", "H"):
            for g in e[col]:
                for v in g["voci"]:
                    m = PUNTEGGIO.search(v)
                    g.setdefault("punteggi", []).append(
                        [int(m.group(1)), int(m.group(2)), int(m.group(3))] if m else None
                    )

    Path(dest).write_text(json.dumps({"elementi": elementi}, ensure_ascii=False, indent=1), "utf8")
    tot = sum(len(g["voci"]) for e in elementi for c in ("D", "H") for g in e[c])
    print(f"{len(elementi)} elementi, {tot} voci di tendina → {dest}")


if __name__ == "__main__":
    main(sys.argv[1] if len(sys.argv) > 1 else "/tmp/valutazione_master.json")
