#!/usr/bin/env python3
"""Vérifie l'intégrité d'un kit Maestro (SHA-256).

Lit SHA256SUMS.txt (format GNU : `<hex> *<filename>` ou `<hex>  <filename>`)
et recalcule le hash de chaque fichier listé. Aucun octet ne doit bouger.

Usage :
  python3 verify_kit_integrity.py
  python3 verify_kit_integrity.py --dir /chemin/vers/kit
  python3 verify_kit_integrity.py --sums SHA256SUMS.txt

Exit 0 = tous VERT. Exit 1 = au moins un mismatch / fichier manquant.
"""

from __future__ import annotations

import argparse
import hashlib
import sys
from pathlib import Path


def parse_sums(path: Path) -> list[tuple[str, str]]:
    rows: list[tuple[str, str]] = []
    for raw in path.read_text(encoding="utf-8").splitlines():
        line = raw.strip()
        if not line or line.startswith("#"):
            continue
        if " *" in line:
            digest, name = line.split(" *", 1)
        else:
            parts = line.split(None, 1)
            if len(parts) != 2:
                raise ValueError(f"ligne SHA256SUMS invalide : {raw!r}")
            digest, name = parts
            if name.startswith("*"):
                name = name[1:]
        digest = digest.strip().lower()
        name = name.strip()
        if len(digest) != 64 or any(c not in "0123456789abcdef" for c in digest):
            raise ValueError(f"digest invalide : {digest!r}")
        if not name or "/" in name or "\\" in name or name in (".", ".."):
            raise ValueError(f"nom de fichier refusé : {name!r}")
        rows.append((digest, name))
    return rows


def sha256_file(path: Path) -> str:
    h = hashlib.sha256()
    with path.open("rb") as f:
        for chunk in iter(lambda: f.read(1024 * 1024), b""):
            h.update(chunk)
    return h.hexdigest()


def main() -> int:
    parser = argparse.ArgumentParser(description="Vérifie SHA-256 d'un kit Maestro")
    parser.add_argument(
        "--dir",
        type=Path,
        default=None,
        help="dossier du kit (défaut : dossier de ce script ou cwd)",
    )
    parser.add_argument(
        "--sums",
        type=Path,
        default=None,
        help="fichier SHA256SUMS.txt (défaut : <dir>/SHA256SUMS.txt)",
    )
    args = parser.parse_args()

    if args.dir is not None:
        root = args.dir.resolve()
    else:
        here = Path(__file__).resolve().parent
        # Si le script est dans le kit, vérifier le kit ; sinon cwd
        if (here / "SHA256SUMS.txt").is_file():
            root = here
        else:
            root = Path.cwd().resolve()

    sums_path = (args.sums or (root / "SHA256SUMS.txt")).resolve()
    if not sums_path.is_file():
        print(f"FAIL  SHA256SUMS introuvable : {sums_path}", file=sys.stderr)
        return 1

    try:
        rows = parse_sums(sums_path)
    except ValueError as err:
        print(f"FAIL  {err}", file=sys.stderr)
        return 1

    if not rows:
        print("FAIL  SHA256SUMS vide", file=sys.stderr)
        return 1

    print(f"kit={root}")
    print(f"sums={sums_path.name}  entries={len(rows)}")
    print("---")

    ok = 0
    bad = 0
    for expected, name in rows:
        path = root / name
        if not path.is_file():
            print(f"MISS  {name}")
            bad += 1
            continue
        actual = sha256_file(path)
        if actual == expected:
            print(f"OK    {name}")
            ok += 1
        else:
            print(f"FAIL  {name}")
            print(f"      attendu={expected}")
            print(f"      obtenu ={actual}")
            bad += 1

    print("---")
    print(f"result ok={ok} fail={bad} total={len(rows)}")
    if bad:
        print("INTÉGRITÉ ROMPUE — au moins un octet a bougé (ou fichier manquant).")
        return 1
    print("INTÉGRITÉ VERT — aucun octet listé n'a bougé.")
    return 0


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