"""Il gestionale dei contatti da riga di comando (29/9/2026). Scrive a nome di Paolo, con la sua chiave API.

    python3 codice/gestionale.py ricontatti [--entro 2026-10-08] [--di valentina]
    python3 codice/gestionale.py cerca "horrible"
    python3 codice/gestionale.py editore "Horrible Guild"            # scheda completa con la cronologia
    python3 codice/gestionale.py stato "Horrible Guild" attesa --ricontatto 2026-10-09
    python3 codice/gestionale.py contatto "Horrible Guild" inviato mail "Chiesto Railroad Tiles in inglese" [--data 2026-09-29]
    python3 codice/gestionale.py persona "Lorenzo Silva"
    python3 codice/gestionale.py gioco 418062
    python3 codice/gestionale.py follow carica [--dal 2026-09-26]    # i ricambiati dal giro follow (dati/follow_reciproci.json)
    python3 codice/gestionale.py follow [--di valentina] [--da-seguire]
    python3 codice/gestionale.py follow seguito <handle>             # dopo aver seguito un account di Valentina

    python3 codice/gestionale.py utenti [--rigenera paolo|valentina]   # crea gli accessi (una volta sola)
    python3 codice/gestionale.py utenti --nuova-chiave valentina      # cambia solo la chiave API (password e login restano)

Le API sono su https://bg-perspective.it/api/gestionale (guida: GET senza chiave). Le credenziali stanno in
`sito/.env.gestionale`, fuori da git, e non si stampano mai: `utenti` le scrive solo lì.
"""
import argparse, hashlib, json, os, secrets, sys, urllib.error, urllib.parse, urllib.request
from datetime import datetime, timezone

QUI = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASE = os.environ.get("GESTIONALE_URL", "https://bg-perspective.it/api/gestionale")
CRED = os.path.join(QUI, "sito", ".env.gestionale")


def env(percorso):
    d = {}
    if os.path.exists(percorso):
        for r in open(percorso, encoding="utf-8"):
            if "=" in r and not r.startswith("#"):
                k, v = r.strip().split("=", 1); d[k] = v
    return d


def chiama(metodo, percorso, corpo=None):
    chiave = env(CRED).get("GESTIONALE_CHIAVE_PAOLO")
    if not chiave: raise SystemExit("manca GESTIONALE_CHIAVE_PAOLO in sito/.env.gestionale (prima: gestionale.py utenti)")
    req = urllib.request.Request(BASE + percorso, method=metodo, data=json.dumps(corpo).encode() if corpo is not None else None,
                                 headers={"Authorization": f"Bearer {chiave}", "Content-Type": "application/json"})
    try:
        with urllib.request.urlopen(req, timeout=30) as r: return json.load(r)
    except urllib.error.HTTPError as e:
        try: messaggio = json.load(e).get("errore")
        except Exception: messaggio = e.reason
        raise SystemExit(f"{e.code}: {messaggio}")


def q(nome): return urllib.parse.quote(str(nome), safe="")


def stampa_editore(e):
    print(f"{e['nome']}  (id {e['id']})")
    for k in ("instagram", "email", "canale", "sito"):
        if e.get(k): print(f"  {k}: {e[k]}")
    for u, s in e["stati"].items():
        print(f"  {u}: {s['stato'] or '—'}" + (f", ricontatto {s['ricontatto_il']}" if s.get("ricontatto_il") else ""))
    if e.get("scheda"): print("  scheda:", e["scheda"])
    if e.get("giochi"): print("  giochi:", ", ".join(f"{g['titolo']} ({g['ruolo']})" for g in e["giochi"]))
    for c in e.get("contatti", []):
        print(f"  [{c['data']}] {c['utente']} {c['tipo']} {c.get('canale') or ''} (#{c['id']}): {c['testo']}")


# --- accessi -----------------------------------------------------------------

def hash_password(pw):
    sale = secrets.token_bytes(16)
    h = hashlib.scrypt(pw.encode(), salt=sale, n=16384, r=8, p=1, dklen=64)
    return f"scrypt${sale.hex()}${h.hex()}"


def nuova_chiave(uid):
    sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
    from gestionale_import import turso
    chiave = secrets.token_urlsafe(32)
    turso([("update g_utenti set token_hash = ? where id = ?", [hashlib.sha256(chiave.encode()).hexdigest(), uid])])
    cred = env(CRED)
    cred[f"GESTIONALE_CHIAVE_{uid.upper()}"] = chiave
    with open(CRED, "w", encoding="utf-8") as f:
        f.write("# Accessi al gestionale (bg-perspective.it/gestionale). Fuori da git, non si stampa mai.\n")
        for k in sorted(cred): f.write(f"{k}={cred[k]}\n")
    os.chmod(CRED, 0o600)
    print(f"nuova chiave API per {uid} in sito/.env.gestionale (la vecchia non vale più)")


def utenti(rigenera=None):
    sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
    from gestionale_import import turso, righe
    esistenti = {r["id"] for r in righe(turso([("select id from g_utenti", [])])[0])}
    cred = env(CRED)
    fatti = []
    for uid, nome in (("paolo", "Paolo"), ("valentina", "Valentina")):
        if uid in esistenti and rigenera != uid: continue
        pw = secrets.token_urlsafe(12)
        chiave = secrets.token_urlsafe(32)
        adesso = datetime.now(timezone.utc).isoformat(timespec="seconds")
        turso([("insert into g_utenti (id, nome, pass_hash, token_hash, creato_il) values (?, ?, ?, ?, ?) "
                "on conflict(id) do update set pass_hash = excluded.pass_hash, token_hash = excluded.token_hash",
                [uid, nome, hash_password(pw), hashlib.sha256(chiave.encode()).hexdigest(), adesso]),
               ("delete from g_sessioni where utente = ?", [uid])])
        cred[f"GESTIONALE_PASSWORD_{uid.upper()}"] = pw
        cred[f"GESTIONALE_CHIAVE_{uid.upper()}"] = chiave
        fatti.append(uid)
    if fatti:
        with open(CRED, "w", encoding="utf-8") as f:
            f.write("# Accessi al gestionale (bg-perspective.it/gestionale). Fuori da git, non si stampa mai.\n")
            for k in sorted(cred): f.write(f"{k}={cred[k]}\n")
        os.chmod(CRED, 0o600)
    print("utenti creati o rigenerati:", ", ".join(fatti) or "nessuno", "· credenziali in sito/.env.gestionale")


# --- comandi -----------------------------------------------------------------

def main():
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    sub = ap.add_subparsers(dest="cmd", required=True)
    s = sub.add_parser("ricontatti"); s.add_argument("--entro"); s.add_argument("--di")
    s = sub.add_parser("cerca"); s.add_argument("testo")
    s = sub.add_parser("editore"); s.add_argument("nome")
    s = sub.add_parser("stato"); s.add_argument("nome"); s.add_argument("stato", choices=["attesa", "si", "no", "nessuno"]); s.add_argument("--ricontatto")
    s = sub.add_parser("contatto"); s.add_argument("nome"); s.add_argument("tipo", choices=["inviato", "ricevuto", "nota"]); s.add_argument("canale"); s.add_argument("testo"); s.add_argument("--data")
    s = sub.add_parser("persona"); s.add_argument("nome")
    s = sub.add_parser("gioco"); s.add_argument("bgg_id", type=int)
    s = sub.add_parser("follow"); s.add_argument("azione", nargs="?", default="elenco", choices=["elenco", "carica", "seguito"]); s.add_argument("handle", nargs="?"); s.add_argument("--dal"); s.add_argument("--di"); s.add_argument("--da-seguire", action="store_true")
    s = sub.add_parser("utenti"); s.add_argument("--rigenera", choices=["paolo", "valentina"]); s.add_argument("--nuova-chiave", choices=["paolo", "valentina"])
    a = ap.parse_args()

    if a.cmd == "utenti": return nuova_chiave(a.nuova_chiave) if a.nuova_chiave else utenti(a.rigenera)
    if a.cmd == "ricontatti":
        par = {k: v for k, v in (("entro", a.entro), ("utente", a.di)) if v}
        for r in chiama("GET", "/ricontatti" + ("?" + urllib.parse.urlencode(par) if par else "")):
            print(f"{r['ricontatto_il']}  {r['nome']}  ({r['stato'] or '—'})")
    elif a.cmd == "follow":
        if a.azione == "carica":
            stato = json.load(open(os.path.join(QUI, "dati", "follow_reciproci.json"), encoding="utf-8"))["account"]
            nomi = {"en": "inglese", "en2": "inglese", "en3": "inglese", "it": "italiano", "de": "tedesco", "de_du": "tedesco",
                    "es": "spagnolo", "es_tu": "spagnolo", "es_ustedes": "spagnolo", "fr": "francese", "fr_tu": "francese",
                    "pt": "portoghese", "pt_br": "portoghese", "cs": "ceco", "tr": "turco", "ru": "russo"}
            account = []
            for h, v in stato.items():
                if v.get("stato") != "ricambiato" or not v.get("dm_il"): continue
                quando = v.get("ricambiato_il") or v["dm_il"]
                if a.dal and quando < a.dal: continue
                account.append({"handle": h, "ricambiato_il": quando, "lingua": nomi.get(v.get("lingua") or "", v.get("lingua")),
                                "descrizione": v.get("descrizione"), "nota": f"DM del {v['dm_il']}"})
            if not account: print("nessun ricambiato da caricare"); return
            print(chiama("POST", "/follow", {"account": account}))
        elif a.azione == "seguito":
            if not a.handle: raise SystemExit("serve l'handle")
            h = a.handle.lstrip("@").lower()
            trovati = [f for f in chiama("GET", "/follow?di=valentina") if f["handle"] == h]
            if not trovati: raise SystemExit(f"@{h} non è nell'elenco di Valentina")
            print(chiama("PATCH", f"/follow/{trovati[0]['id']}", {"seguito": True}))
        else:
            par = "&".join(p for p in [f"di={a.di}" if a.di else "", "da_seguire=1" if a.da_seguire else ""] if p)
            for f in chiama("GET", "/follow" + (f"?{par}" if par else "")):
                print(f"{f['utente']:10} @{f['handle']:30} {f.get('lingua') or '':10} {f['ricambiato_il']}  "
                      f"{'seguito ' + f['seguito_il'] if f.get('seguito_il') else 'da seguire'}  {f.get('descrizione') or ''}")
    elif a.cmd == "cerca":
        for e in chiama("GET", "/editori?cerca=" + q(a.testo)):
            print(f"editore {e['id']}: {e['nome']}  P={e['stati']['paolo']['stato'] or '—'} V={e['stati']['valentina']['stato'] or '—'}")
        for p in chiama("GET", "/persone?cerca=" + q(a.testo)):
            print(f"persona {p['id']}: {p['nome']} {p.get('instagram') or ''}")
        for g in chiama("GET", "/giochi?cerca=" + q(a.testo))[:20]:
            print(f"gioco {g['bgg_id']}: {g['titolo']}")
    elif a.cmd == "editore":
        stampa_editore(chiama("GET", "/editori/" + q(a.nome)))
    elif a.cmd == "stato":
        corpo = {"stato": "" if a.stato == "nessuno" else a.stato}
        if a.ricontatto is not None: corpo["ricontatto_il"] = a.ricontatto or None
        print(json.dumps(chiama("PUT", f"/editori/{q(a.nome)}/stato", corpo), ensure_ascii=False))
    elif a.cmd == "contatto":
        corpo = {"tipo": a.tipo, "canale": a.canale, "testo": a.testo}
        if a.data: corpo["data"] = a.data
        print("registrato, id", chiama("POST", f"/editori/{q(a.nome)}/contatti", corpo)["id"])
    elif a.cmd == "persona":
        trovate = chiama("GET", "/persone?cerca=" + q(a.nome))
        if not trovate: raise SystemExit("nessuna persona trovata")
        p = chiama("GET", f"/persone/{trovate[0]['id']}")
        print(f"{p['nome']} (id {p['id']}) {p.get('instagram') or ''}")
        if p.get("scheda"): print("  scheda:", p["scheda"])
        for g in p["giochi"]: print(f"  {g['titolo']} ({g['ruolo']})")
        for c in p["contatti"]: print(f"  [{c['data']}] {c['utente']} {c['tipo']}: {c['testo']}")
    elif a.cmd == "gioco":
        g = chiama("GET", f"/giochi/{a.bgg_id}")
        print(f"{g['titolo']} ({g.get('anno') or '?'}) bgg {g['bgg_id']} · {g.get('nostro') or ''}")
        for e in g["editori"]: print(f"  editore: {e['nome']} ({e['ruolo']}) {e.get('instagram') or ''}")
        for p in g["persone"]: print(f"  {p['ruolo']}: {p['nome']} {p.get('instagram') or ''}")


if __name__ == "__main__":
    main()
