#!/usr/bin/env python3
"""Deterministically bind a reviewed manifest to an integration repository.

This utility reads local Git objects and writes JSON only.  It never uses a
wallet, RPC endpoint, signing key, deployment API, or broadcast transport.
"""
from __future__ import annotations

import argparse
import hashlib
import json
import subprocess
from copy import deepcopy
from pathlib import Path
from typing import Any

from deployment_manifest import ManifestValidationError, canonical_json, load_json, validate_manifest


def _git(repository: Path, *args: str) -> str:
    try:
        return subprocess.check_output(["git", "-C", str(repository), *args], text=True).strip()
    except (OSError, subprocess.CalledProcessError) as exc:
        raise ManifestValidationError(f"cannot read reviewed Git object: {exc}") from exc


def _archive_sha256(repository: Path, commit: str, *paths: str) -> str:
    try:
        archive = subprocess.check_output(["git", "-C", str(repository), "archive", "--format=tar", commit, *paths])
    except (OSError, subprocess.CalledProcessError) as exc:
        raise ManifestValidationError(f"cannot archive reviewed Git paths: {exc}") from exc
    return hashlib.sha256(archive).hexdigest()


def _file_sha256(path: Path, label: str) -> str:
    try:
        digest = hashlib.sha256()
        with path.open("rb") as handle:
            for chunk in iter(lambda: handle.read(1024 * 1024), b""):
                digest.update(chunk)
    except OSError as exc:
        raise ManifestValidationError(f"cannot hash {label}: {exc}") from exc
    return digest.hexdigest()


def finalize_manifest(
    template: dict[str, Any],
    repository: Path,
    source_commit: str,
    evidence_commit: str,
    reviewer_bundle: Path,
    validation_archive: Path,
) -> dict[str, Any]:
    validate_manifest(template)
    if template.get("status") != "pre-deployment-review":
        raise ManifestValidationError("only an unchanged pre-deployment-review template may be finalized")
    # Resolve first: a short ref or an unavailable commit never enters the output.
    source_commit = _git(repository, "rev-parse", "--verify", f"{source_commit}^{{commit}}")
    evidence_commit = _git(repository, "rev-parse", "--verify", f"{evidence_commit}^{{commit}}")
    evidence_parent = _git(repository, "rev-parse", f"{evidence_commit}^")
    if evidence_parent != source_commit:
        raise ManifestValidationError("evidence commit must be the direct child of the exact source commit")
    result = deepcopy(template)
    result["status"] = "finalization-candidate"
    result["provenance"] = {
        "sourceCommit": source_commit,
        "sourceTree": _git(repository, "rev-parse", f"{source_commit}:v3-src"),
        "contractsTree": _git(repository, "rev-parse", f"{source_commit}:v3-src/contracts"),
        "evidenceCommit": evidence_commit,
        "evidenceTree": _git(repository, "rev-parse", f"{evidence_commit}:release-evidence"),
        "sourceArchiveSha256": _archive_sha256(repository, source_commit, "v3-src"),
        "evidenceArchiveSha256": _archive_sha256(repository, evidence_commit, "release-evidence", "audit-evidence"),
        "reviewerBundleSha256": _file_sha256(reviewer_bundle, "reviewer bundle"),
        "validationArchiveSha256": _file_sha256(validation_archive, "validation archive"),
    }
    payload = deepcopy(result)
    payload.pop("integrity", None)
    result["integrity"]["manifestPayloadSha256"] = hashlib.sha256(canonical_json(payload)).hexdigest()
    validate_manifest(result)
    return result


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description="Offline deterministic bonds-only manifest finalizer.")
    parser.add_argument("--manifest", type=Path, default=Path(__file__).with_name("base_v3_deployment_manifest.json"))
    parser.add_argument("--repository", type=Path, required=True, help="Local reviewed Git repository; no network access is performed.")
    parser.add_argument("--source-commit", required=True, help="Reviewed source commit or ref.")
    parser.add_argument("--evidence-commit", required=True, help="Reviewed evidence commit or ref.")
    parser.add_argument("--reviewer-bundle", type=Path, required=True, help="Exact-commit reviewer archive to hash.")
    parser.add_argument("--validation-archive", type=Path, required=True, help="Unedited validation archive to hash.")
    parser.add_argument("--out", type=Path, required=True)
    args = parser.parse_args(argv)
    try:
        finalized = finalize_manifest(
            load_json(args.manifest),
            args.repository,
            args.source_commit,
            args.evidence_commit,
            args.reviewer_bundle,
            args.validation_archive,
        )
    except ManifestValidationError as exc:
        parser.error(f"FAIL-CLOSED: {exc}")
    args.out.parent.mkdir(parents=True, exist_ok=True)
    args.out.write_text(json.dumps(finalized, indent=2) + "\n", encoding="utf-8")
    print(json.dumps({"written": str(args.out), "status": "finalization-candidate", "operations": "none"}))
    return 0


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