#!/usr/bin/env python3
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
# Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
# Source-Disclosed License — All rights reserved except as expressly granted.
# See LICENCE-CMSD-1.0.md for terms.
"""premier-pasctl — le moteur du premier démarrage (#1522).

  premier-pasctl examiner  [--profil F]   ce qui est prêt, manque ou est faux
  premier-pasctl plan      [--profil F]   les actions exactes, sans rien faire
  premier-pasctl appliquer [--profil F]   applique (root) ; s'arrête à la 1re erreur
  premier-pasctl reponses --fichier F --signature S [--trousseau K]
                                          vérifie un fichier de réponses Auto-Load (#2184) :
                                          signature, schéma strict, secrets par référence
  premier-pasctl etat                     où en est la box (lu par les faces)
  premier-pasctl silencieux               au démarrage : profil prêt → appliqué ;
                                          sinon l'assistant s'ouvrira à l'étape manquante
  premier-pasctl appliquer-demande        (unité .path, root) applique le profil
                                          rempli par une face, à sa demande
Sans --profil : secubox.profil= sur la ligne noyau, puis /boot/secubox/profil.toml…
"""
import argparse
import json
import sys
from pathlib import Path

sys.path.insert(0, "/usr/lib/secubox/premier-pas")
_ici = Path(__file__).resolve().parents[1]
if (_ici / "premier_pas").is_dir():
    sys.path.insert(0, str(_ici))

from premier_pas import moteur as M, profil as P, provision as V  # noqa: E402

TROUSSEAU_DEFAUT = Path("/usr/share/secubox/autoload/provisioning.gpg")


def _profil(args):
    chemin = Path(args.profil) if args.profil else M.cherche_profil()
    if not chemin:
        print("aucun profil trouvé (secubox.profil=, /boot/secubox/profil.toml, clé SBXPROFIL)", file=sys.stderr)
        sys.exit(2)
    try:
        return chemin, P.lit(chemin)
    except Exception as e:  # noqa: BLE001
        print(f"{chemin} illisible : {e}", file=sys.stderr)
        sys.exit(2)


def main(argv=None):
    ap = argparse.ArgumentParser(prog="premier-pasctl", description=__doc__,
                                 formatter_class=argparse.RawDescriptionHelpFormatter)
    sub = ap.add_subparsers(dest="cmd", required=True)
    for c in ("examiner", "plan", "appliquer"):
        s = sub.add_parser(c)
        s.add_argument("--profil")
        s.add_argument("--json", action="store_true")
    r = sub.add_parser("reponses")
    r.add_argument("--fichier", required=True)
    r.add_argument("--signature", required=True)
    r.add_argument("--trousseau", default=str(TROUSSEAU_DEFAUT))
    r.add_argument("--json", action="store_true")
    sub.add_parser("etat")
    sub.add_parser("silencieux")
    sub.add_parser("appliquer-demande")
    a = ap.parse_args(argv)

    if a.cmd == "etat":
        fait = M.MARQUEUR.exists()
        try:
            etat = json.loads(M.ETAT.read_text())
        except (OSError, ValueError):
            etat = {}
        print(json.dumps({"fait": fait, **etat}, ensure_ascii=False, indent=1))
        return 0

    if a.cmd == "reponses":
        try:
            ex = V.charger_signe(Path(a.fichier), Path(a.signature), Path(a.trousseau))
        except (V.SignatureInvalide, V.FichierInvalide) as e:
            print(json.dumps({"valide": False, "refus": str(e)}, ensure_ascii=False) if a.json else f"refusé : {e}", file=sys.stderr if not a.json else sys.stdout)
            return 4
        out = {"valide": ex.complet, "manquantes": ex.manquantes, "erreurs": ex.erreurs}
        print(json.dumps(out, ensure_ascii=False, indent=1) if a.json else ("accepté" if ex.complet else f"incomplet : {out}"))
        return 0 if ex.complet else 3

    if a.cmd == "appliquer-demande":
        demande = Path("/var/lib/secubox/premier-pas/demande")
        profil = Path("/var/lib/secubox/premier-pas/profil.toml")
        if not demande.exists() or M.MARQUEUR.exists():
            demande.unlink(missing_ok=True)
            return 0
        demande.unlink()                         # une demande = une tentative
        r = M.appliquer(P.lit(profil))
        return 0 if r.ok else 1

    if a.cmd == "silencieux":
        if M.MARQUEUR.exists():
            return 0
        M.pose_jeton()
        chemin = M.cherche_profil()
        if not chemin:
            M.ecrit_etat(phase="assistant", reprendre="bienvenue", manquantes=list(P.ETAPES[1:-1]))
            M.journal("aucun profil prêt : l'assistant s'ouvrira")
            return 0
        M.journal(f"profil prêt : {chemin}")
        r = M.appliquer(P.lit(chemin))
        return 0 if (r.ok or r.reprendre) else 1

    chemin, prof = _profil(a)
    if a.cmd == "examiner":
        ex = P.examine(prof)
        out = {"profil": str(chemin), "complet": ex.complet, "manquantes": ex.manquantes,
               "erreurs": ex.erreurs, "reprendre": ex.premiere_etape}
        if a.json:
            print(json.dumps(out, ensure_ascii=False, indent=1))
        else:
            for e in P.ETAPES:
                marque = "✗ " + ex.erreurs[e] if e in ex.erreurs else ("… à compléter" if e in ex.manquantes else "✓")
                print(f"  {P.LIBELLES[e]:<20} {marque}")
            print(f"\n{'complet : applicable sans écran' if ex.complet else 'reprendre à : ' + P.LIBELLES[ex.premiere_etape]}")
        return 0 if ex.complet else 3

    if a.cmd == "plan":
        ex = P.examine(prof)
        if not ex.complet:
            print(f"profil incomplet — reprendre à « {P.LIBELLES[ex.premiere_etape]} »", file=sys.stderr)
            return 3
        for i, act in enumerate(M.plan(prof), 1):
            cmd = " ".join(act.argv) if act.argv else ("(interne)" if act.fn else "(information)")
            print(f"{i:>2}. [{P.LIBELLES[act.etape]}] {act.quoi}\n      {cmd}")
        return 0

    r = M.appliquer(prof)
    if r.ok:
        print(f"fait : {len(r.faites)} actions ; marqueur {M.MARQUEUR}")
        return 0
    print(f"arrêt à « {P.LIBELLES.get(r.reprendre or '', r.reprendre)} » : {r.detail}", file=sys.stderr)
    return 1


if __name__ == "__main__":
    sys.exit(main())
