#!/usr/bin/env python3
"""Create and verify immutable SHiRE Academy state snapshots.

Operational pause/resume fields in autopilot_state.json are intentionally
excluded. Curriculum, qualification, certification and Forge-related fields
remain protected.
"""
from __future__ import annotations

import argparse
import hashlib
import json
import sys
from pathlib import Path
from typing import Any

ALLOWED_AUTOPILOT_CHANGES = frozenset({
    "enabled",
    "paused",
    "review_required",
    "safety_stop",
    "safety_stop_reason",
    "updated_at",
})


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


def load_json(path: Path) -> Any:
    return json.loads(path.read_text(encoding="utf-8"))


def immutable_autopilot(path: Path) -> dict[str, Any]:
    value = load_json(path)
    if not isinstance(value, dict):
        raise ValueError(f"Invalid autopilot JSON object: {path}")
    return {
        key: child
        for key, child in value.items()
        if key not in ALLOWED_AUTOPILOT_CHANGES
    }


def canonical_sha256(payload: Any) -> str:
    raw = json.dumps(
        payload,
        sort_keys=True,
        separators=(",", ":"),
        ensure_ascii=False,
    ).encode("utf-8")
    return hashlib.sha256(raw).hexdigest()


def snapshot(args: argparse.Namespace) -> dict[str, Any]:
    practice = Path(args.practice)
    autopilot = Path(args.autopilot)
    sprint = Path(args.sprint)
    index = Path(args.index)
    for path in (practice, autopilot, sprint, index):
        if not path.is_file():
            raise FileNotFoundError(path)
    immutable = immutable_autopilot(autopilot)
    return {
        "schema": "shire.academy.immutable_state_snapshot.v1",
        "allowed_autopilot_changes": sorted(ALLOWED_AUTOPILOT_CHANGES),
        "files": {
            str(practice): {"mode": "full-file", "sha256": sha256_file(practice)},
            str(autopilot): {
                "mode": "immutable-json",
                "sha256": canonical_sha256(immutable),
                "immutable_payload": immutable,
            },
            str(sprint): {"mode": "full-file", "sha256": sha256_file(sprint)},
            str(index): {"mode": "full-file", "sha256": sha256_file(index)},
        },
    }


def write_snapshot(payload: dict[str, Any], output: Path) -> None:
    output.parent.mkdir(parents=True, exist_ok=True)
    output.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("command", choices=("snapshot", "verify"))
    parser.add_argument("--practice", required=True)
    parser.add_argument("--autopilot", required=True)
    parser.add_argument("--sprint", required=True)
    parser.add_argument("--index", required=True)
    parser.add_argument("--output")
    parser.add_argument("--expected")
    args = parser.parse_args()

    current = snapshot(args)
    if args.command == "snapshot":
        if not args.output:
            parser.error("snapshot requires --output")
        write_snapshot(current, Path(args.output))
        print(f"PASS: Immutable Academy snapshot written: {args.output}")
        return 0

    if not args.expected:
        parser.error("verify requires --expected")
    expected = load_json(Path(args.expected))
    if expected != current:
        print("FAIL: Immutable Academy state differs from the expected snapshot.", file=sys.stderr)
        expected_files = expected.get("files", {}) if isinstance(expected, dict) else {}
        current_files = current.get("files", {})
        for path in sorted(set(expected_files) | set(current_files)):
            if expected_files.get(path) != current_files.get(path):
                print(f"CHANGED: {path}", file=sys.stderr)
                print(
                    "  expected: " + json.dumps(expected_files.get(path), sort_keys=True),
                    file=sys.stderr,
                )
                print(
                    "  current:  " + json.dumps(current_files.get(path), sort_keys=True),
                    file=sys.stderr,
                )
        return 1
    print("PASS: Immutable Academy state is unchanged.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
