#!/usr/bin/env python3
"""Estrae la didascalia del LUOGO SICURO ESTERNO dal foglio "Conclusione" di
ogni DVR xlsx e la prepara per popolare dvr.luogo_sicuro_esterno_descr.

Import one-off (round feedback cliente 2026-07-07). NON scrive sul DB: produce
un JSON {source_file -> testo} + un report. La scrittura su dev.db e prod è
gestita a valle dall'applicatore node/libsql (stessa chiave: source_file, che
coincide bit-a-bit tra dev.db e prod).

Regola di estrazione (validata su tutti i 603 xlsx dall'analisi campione):
- foglio "Conclusione": la didascalia è una cella (quasi sempre col A, righe
  53-56) con prefisso "LUOGO SICURO ESTERNO - <indirizzo>" (o varianti).
- va distinta dall'etichetta fissa "INDICAZIONE LUOGO SICURO ESTERNO".
- se c'e' il prefisso ma l'indirizzo e' vuoto/placeholder -> lascia vuoto (no fallback).
- fallback strutturale (indirizzo "nudo" a riga_etichetta+~23) -> tier2, da rivedere.
- i docx (197) non contengono mai l'indirizzo -> restano vuoti/editabili a mano.

Uso:
  python3 import-tools/scripts/import_luogo_sicuro_descr.py \
      --db import-tools/db/dev.db --dvr-dir -mat/DVR --out /path/didascalie.json
"""
import argparse
import json
import os
import re
import sqlite3
import sys

from openpyxl import load_workbook

LABEL = re.compile(r"^\s*INDICAZIONE\s+LUOGO\s+SICURO\s+ESTERNO\s*$", re.I)
CAP = re.compile(
    r"^\s*LUOGO\s+(?:SICURO\s+ESTERNO|ESTERNO\s+SICURO|SICURO)\b\s*[-–:]?\s*(.*)$",
    re.I,
)
PLACEHOLDER = re.compile(r"nomepiazza|nomevia|xxxx", re.I)
JUNK = {"t", "all. 2", "all. 4"}


def estrai(ws):
    """Ritorna (testo|None, tier). tier in {tier1, tier2, vuoto}."""
    label_row = None
    cells = {}
    tier1 = None
    cap_seen = False
    for row in ws.iter_rows():
        for c in row:
            if c.value is None:
                continue
            s = str(c.value).strip()
            if not s:
                continue
            cells[(c.row, c.column)] = s
            if LABEL.match(s):
                if label_row is None:
                    label_row = c.row
                continue
            if tier1 is None and not cap_seen:
                m = CAP.match(s)
                if m and "LUOGO" in s.upper():
                    cap_seen = True
                    cand = m.group(1).strip(" -–:\t")
                    if cand and not PLACEHOLDER.search(cand):
                        tier1 = cand
    if tier1:
        return tier1, "tier1"
    if cap_seen:
        # prefisso presente ma indirizzo vuoto/placeholder -> vuoto (niente fallback)
        return None, "vuoto"
    if label_row:
        for d in (23, 22, 25, 24, 21):
            for col in (1, 2):
                v = cells.get((label_row + d, col))
                if (
                    v
                    and not LABEL.match(v)
                    and v.lower() not in JUNK
                    and not PLACEHOLDER.search(v)
                ):
                    return v, "tier2"
    return None, "vuoto"


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--db", default="import-tools/db/dev.db")
    ap.add_argument("--dvr-dir", default="-mat/DVR")
    ap.add_argument("--out", default="didascalie.json")
    ap.add_argument("--apply", action="store_true", help="scrive luogo_sicuro_esterno_descr su --db")
    args = ap.parse_args()

    con = sqlite3.connect(args.db)
    con.row_factory = sqlite3.Row
    rows = con.execute(
        "SELECT id, source_file FROM dvr WHERE source_format = 'xlsx' AND source_file IS NOT NULL"
    ).fetchall()
    con.close()

    out = {}
    stats = {"tier1": 0, "tier2": 0, "vuoto": 0, "no_conclusione": 0, "errore": 0}
    tier2_list = []
    errori = []

    for r in rows:
        sf = r["source_file"]
        path = os.path.join(args.dvr_dir, sf)
        if not os.path.exists(path):
            stats["errore"] += 1
            errori.append((sf, "file mancante"))
            continue
        try:
            wb = load_workbook(path, data_only=True, read_only=True)
        except Exception as e:  # xlsx corrotto/malformato
            stats["errore"] += 1
            errori.append((sf, f"open: {type(e).__name__}"))
            continue
        ws = None
        for name in wb.sheetnames:
            if name.strip().lower() == "conclusione":
                ws = wb[name]
                break
        if ws is None:
            stats["no_conclusione"] += 1
            wb.close()
            continue
        testo, tier = estrai(ws)
        wb.close()
        stats[tier] += 1
        if tier == "tier2":
            tier2_list.append((sf, testo))
        if testo:
            out[sf] = {"id": r["id"], "testo": testo, "tier": tier}

    with open(args.out, "w", encoding="utf-8") as fh:
        json.dump(out, fh, ensure_ascii=False, indent=1)

    if args.apply:
        import datetime
        now = datetime.datetime.now(datetime.timezone.utc).isoformat()
        con = sqlite3.connect(args.db)
        applied = 0
        for sf, v in out.items():
            cur = con.execute(
                "UPDATE dvr SET luogo_sicuro_esterno_descr = ?, updated_at = ? WHERE id = ?",
                (v["testo"], now, v["id"]),
            )
            applied += cur.rowcount
        con.commit()
        con.close()
        print(f"\n[APPLY] righe aggiornate su {args.db}: {applied}")

    print(f"DVR xlsx analizzati : {len(rows)}")
    print(f"  tier1 (affidabile): {stats['tier1']}")
    print(f"  tier2 (da rivedere): {stats['tier2']}")
    print(f"  vuoto             : {stats['vuoto']}")
    print(f"  senza foglio Concl: {stats['no_conclusione']}")
    print(f"  errore/mancante   : {stats['errore']}")
    print(f"  --> didascalie da importare (tier1+tier2): {len(out)}")
    print(f"  JSON scritto in: {args.out}")
    if tier2_list:
        print("\n--- TIER2 (da verificare a mano) ---")
        for sf, t in tier2_list:
            print(f"  [{sf}] -> {t!r}")
    if errori:
        print("\n--- ERRORI/MANCANTI ---")
        for sf, e in errori:
            print(f"  [{sf}] {e}")


if __name__ == "__main__":
    main()
