#!/usr/bin/env python3
"""Generate deterministic SHA-256 bindings for the uncommitted operations candidate."""

from __future__ import annotations

import hashlib
import json
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
OUTPUT = ROOT / "v3-operations/policy-commitment.json"
SOLIDITY = ROOT / "v3-operations/contracts/IV3GovernancePolicyCommitment.sol"
ARTIFACTS = [
    "v3-operations/governance-selector-policy.json",
    "v3-operations/role-control-policy.json",
    "v3-operations/operations-control.json",
    "v3-operations/release-status.json",
    "v3-operations/readiness-supersession.json",
    "v3-operations/governance-enforcement.json",
    "v3-operations/schemas/release-status.schema.json",
    "v3-operations/schemas/role-control.schema.json",
    "v3-operations/schemas/selector-policy.schema.json",
    "v3-migration/completeness/supported-v3-modules.json",
    "v3-migration/completeness/v2-contract-dispositions.json",
]
IMPLEMENTATION_BINDINGS = [
    "v3-src/contracts/governance/MultisigGovernanceV3.sol",
    "v3-src/test/governance/MultisigGovernanceV3.t.sol",
    "repos/vaultfire-sdk/tests/fixtures/reviewed-artifacts/MultisigGovernanceV3.json",
    "ci/manifests/base-v3-remediation-source.sha256",
    "ci/check_base_v3_source_snapshot.py",
    "ci/manifests/base-v3-governance-policy.json",
]
BASE_SNAPSHOT_MANIFEST = "ci/manifests/base-v3-remediation-source.sha256"
BASE_SNAPSHOT_CHECKER = "ci/check_base_v3_source_snapshot.py"
BASE_SNAPSHOT_FILE_COUNT = 117
LEDGER_BINDINGS = [
    "v3-migration/completeness/ledger.json",
    "v3-migration/completeness/check_ledger.py",
    "v3-migration/completeness/tests/test_checker.py",
    "v3-migration/completeness/artifact-checksums.sha256",
]
AUTHORITY_BINDINGS = [
    "v3-migration/completeness/current-authority-facts.json",
    "repos/vaultfire-sdk/V3_SDK_ABI_RECONCILIATION_20260826.txt",
    "repos/vaultfire-sdk/tests/fixtures/v3-reviewed-abi-signatures.json",
    "repos/vaultfire-sdk/tests/fixtures/reviewed-artifacts/AccountabilityBondV3Safe.json",
    "repos/vaultfire-sdk/tests/fixtures/reviewed-artifacts/PartnershipBondV3Safe.json",
    "repos/vaultfire-sdk/tests/fixtures/reviewed-artifacts/MultisigGovernanceV3.json",
    "repos/vaultfire-sdk/tests/fixtures/reviewed-artifacts/MandateRegistryV3.json",
    "v3-open-spec/ARTIFACTS.sha256",
    "v3-open-spec/mappings/canonical-v3.json",
    "v3-open-spec/interfaces/canonical-v3-interfaces.v1.json",
    "v3-open-spec/reports/conformance-report.json",
    "pr10/partner-package/THIRTEENTH_INDEPENDENT_AUDIT_CLOSURE_20260826.json",
    "pr10/partner-package/manifests/organizational-readiness.template.json",
    "release-evidence/VAULTFIRE_V3_FINAL_CROSS_CHAIN_READINESS_20260822.json",
    "ci/manifests/base-v3-governance-policy.json",
]
EVIDENCE = [
    "v3-operations/evidence/v2-abi-strict-20260825.json",
    "v3-operations/evidence/governance-source-reconciliation-20260825.json",
    "v3-operations/evidence/canonical-ledger-reconciliation-20260826.json",
    "v3-operations/evidence/current-authority-reconciliation-20260826.json",
]


def canonical_json(value) -> bytes:
    # RFC 8785-compatible for this repository's integer/string/boolean/null data
    # model: UTF-8, sorted keys, minimal separators, and no NaN/Infinity/floats.
    return json.dumps(
        value, sort_keys=True, separators=(",", ":"), ensure_ascii=False, allow_nan=False
    ).encode("utf-8")


def strict_load(path: Path):
    def reject_duplicates(pairs):
        result = {}
        for key, value in pairs:
            if key in result:
                raise ValueError(f"duplicate JSON key in {path}: {key}")
            result[key] = value
        return result

    return json.loads(path.read_text(encoding="utf-8"), object_pairs_hook=reject_duplicates)


def digest_bytes(payload: bytes) -> str:
    return hashlib.sha256(payload).hexdigest()


def verify_base_snapshot() -> dict[str, object]:
    manifest_path = ROOT / BASE_SNAPSHOT_MANIFEST
    rows = [
        line.split(maxsplit=1)
        for line in manifest_path.read_text(encoding="utf-8").splitlines()
        if line.strip()
    ]
    if len(rows) != BASE_SNAPSHOT_FILE_COUNT:
        raise ValueError(
            f"Base snapshot must contain exactly {BASE_SNAPSHOT_FILE_COUNT} files, got {len(rows)}"
        )
    paths = [row[1] for row in rows]
    if len(paths) != len(set(paths)):
        raise ValueError("Base snapshot manifest contains duplicate paths")
    for expected, relative in rows:
        path = ROOT / relative
        if not path.is_file() or digest_bytes(path.read_bytes()) != expected:
            raise ValueError(f"Base snapshot mismatch: {relative}")
        if relative.startswith(("v3-migration/", "v3-operations/")):
            raise ValueError(f"mutable downstream authority in Base snapshot: {relative}")
    return {
        "manifestPath": BASE_SNAPSHOT_MANIFEST,
        "manifestSha256": digest_bytes(manifest_path.read_bytes()),
        "checkerPath": BASE_SNAPSHOT_CHECKER,
        "checkerSha256": digest_bytes((ROOT / BASE_SNAPSHOT_CHECKER).read_bytes()),
        "fileCount": BASE_SNAPSHOT_FILE_COUNT,
        "verifiedUnchangedFileCount": BASE_SNAPSHOT_FILE_COUNT,
        "ledgerReconciliationOverrides": [],
    }


def main() -> None:
    artifacts = []
    for relative in ARTIFACTS:
        value = strict_load(ROOT / relative)
        artifacts.append(
            {
                "path": relative,
                "sha256": digest_bytes((ROOT / relative).read_bytes()),
                "canonicalJsonSha256": digest_bytes(canonical_json(value)),
            }
        )
    implementation_bindings = [
        {
            "path": relative,
            "sha256": digest_bytes((ROOT / relative).read_bytes()),
        }
        for relative in IMPLEMENTATION_BINDINGS
    ]
    base_snapshot = verify_base_snapshot()
    ledger_bindings = [
        {"path": relative, "sha256": digest_bytes((ROOT / relative).read_bytes())}
        for relative in LEDGER_BINDINGS
    ]
    authority_bindings = [
        {"path": relative, "sha256": digest_bytes((ROOT / relative).read_bytes())}
        for relative in AUTHORITY_BINDINGS
    ]
    candidate_payload = {
        "canonicalization": "RFC8785-repository-subset-v1",
        "artifacts": artifacts,
        "implementationBindings": implementation_bindings,
        "baseSnapshot": base_snapshot,
        "ledgerBindings": ledger_bindings,
        "authorityBindings": authority_bindings,
    }
    candidate_id = digest_bytes(canonical_json(candidate_payload))
    for relative in EVIDENCE:
        evidence_path = ROOT / relative
        evidence = strict_load(evidence_path)
        evidence["candidateBinding"] = candidate_id
        evidence_path.write_text(
            json.dumps(evidence, indent=2, sort_keys=True) + "\n", encoding="utf-8"
        )
    record = {
        "schemaVersion": "1.4.0",
        "canonicalization": "RFC8785-repository-subset-v1",
        "candidateId": candidate_id,
        "artifacts": artifacts,
        "implementationBindings": implementation_bindings,
        "baseSnapshot": base_snapshot,
        "ledgerBindings": ledger_bindings,
        "authorityBindings": authority_bindings,
        "evidenceBindings": [
            {
                "path": relative,
                "sha256": digest_bytes((ROOT / relative).read_bytes()),
                "candidateBinding": candidate_id,
            }
            for relative in EVIDENCE
        ],
    }
    OUTPUT.write_text(json.dumps(record, indent=2, sort_keys=True) + "\n", encoding="utf-8")
    by_path = {row["path"]: row["canonicalJsonSha256"] for row in artifacts}
    implementation_by_path = {row["path"]: row["sha256"] for row in implementation_bindings}
    solidity = f"""// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

/// @notice Exact integration surface implemented by the V3 governance executor.
/// @dev Release authorization remains a separate, human-controlled gate.
interface IV3GovernancePolicyCommitment {{
    function governancePolicyCommitment() external view returns (bytes32);
    function isGovernanceCallAllowed(address target, uint256 value, bytes calldata data)
        external view returns (bool);
}}

/// @notice Generated constants binding the reviewed policy and source scope.
library V3GovernancePolicyCommitment {{
    bytes32 internal constant POLICY_SHA256 =
        hex"{by_path['v3-operations/governance-selector-policy.json']}";
    bytes32 internal constant BASE_GOVERNANCE_POLICY_SHA256 =
        hex"{digest_bytes(canonical_json(strict_load(ROOT / 'ci/manifests/base-v3-governance-policy.json')))}";
    bytes32 internal constant BASE_GOVERNANCE_POLICY_FILE_SHA256 =
        hex"{implementation_by_path['ci/manifests/base-v3-governance-policy.json']}";
    bytes32 internal constant SUPPORTED_MODULES_SHA256 =
        hex"{by_path['v3-migration/completeness/supported-v3-modules.json']}";
    bytes32 internal constant DISPOSITIONS_SHA256 =
        hex"{by_path['v3-migration/completeness/v2-contract-dispositions.json']}";
    bytes32 internal constant GOVERNANCE_SOURCE_SHA256 =
        hex"{implementation_by_path['v3-src/contracts/governance/MultisigGovernanceV3.sol']}";
    bytes32 internal constant GOVERNANCE_TEST_SHA256 =
        hex"{implementation_by_path['v3-src/test/governance/MultisigGovernanceV3.t.sol']}";
    bytes32 internal constant GOVERNANCE_ARTIFACT_SHA256 =
        hex"{implementation_by_path['repos/vaultfire-sdk/tests/fixtures/reviewed-artifacts/MultisigGovernanceV3.json']}";
    bytes32 internal constant BASE_SNAPSHOT_MANIFEST_SHA256 =
        hex"{implementation_by_path['ci/manifests/base-v3-remediation-source.sha256']}";
    bytes32 internal constant BASE_SNAPSHOT_CHECKER_SHA256 =
        hex"{implementation_by_path['ci/check_base_v3_source_snapshot.py']}";
    bytes32 internal constant CANDIDATE_ID = hex"{candidate_id}";
}}
"""
    SOLIDITY.write_text(solidity, encoding="utf-8")
    print(candidate_id)


if __name__ == "__main__":
    main()
