from __future__ import annotations

import json
import subprocess
import sys
import unittest
from contextlib import contextmanager
from pathlib import Path
from typing import Iterator

ROOT = Path(__file__).resolve().parents[1]
GATE = ROOT / "ci/check_base_read_only_bootstrap_release_gate.py"
STRICT = ROOT / "baseline/check_v2_abi_package.py"


def run(*args: str) -> subprocess.CompletedProcess[str]:
    return subprocess.run([sys.executable, *args], cwd=ROOT, text=True, capture_output=True, check=False)


@contextmanager
def restored(path: Path) -> Iterator[None]:
    original = path.read_bytes()
    try:
        yield
    finally:
        path.write_bytes(original)


class BaseReadOnlyBootstrapReleaseGateTests(unittest.TestCase):
    def test_gate_passes_only_for_the_reviewed_closure_and_requires_global_gate(self) -> None:
        result = run(str(GATE))
        self.assertEqual(result.returncode, 0, result.stderr)
        self.assertIn('"bootstrapSourceCount": 21', result.stdout)
        self.assertIn('"v2IntegrationCount": 2', result.stdout)
        strict = run(str(STRICT), "--strict")
        self.assertEqual(strict.returncode, 0, strict.stderr)
        self.assertIn("verified 36 frozen, provenance-backed V2 ABIs", strict.stdout)

    def test_attempted_legacy_reintroduction_into_bootstrap_source_is_rejected(self) -> None:
        target = ROOT / "v3-src/contracts/bonds/SafeBondFactoryV3.sol"
        with restored(target):
            target.write_text(target.read_text() + "\n// VaultfireTrustAttestation must never enter the bootstrap closure.\n")
            result = run(str(GATE))
        self.assertNotEqual(result.returncode, 0)
        self.assertIn("excluded legacy contract appears", result.stderr)

    def test_dependency_closure_expansion_is_rejected(self) -> None:
        target = ROOT / "v3-src/contracts/bonds/SafeBondFactoryV3.sol"
        with restored(target):
            target.write_text(target.read_text() + '\nimport "../escrow/TaskEscrowV3.sol";\n')
            result = run(str(GATE))
        self.assertNotEqual(result.returncode, 0)
        self.assertIn("bootstrap dependency closure drifted", result.stderr)

    def test_v2_integration_manifest_drift_is_rejected(self) -> None:
        target = ROOT / "pr8/pilot/base_v3_deployment_manifest.json"
        with restored(target):
            payload = json.loads(target.read_text())
            payload["v2Compatibility"]["integrations"]["missionEnforcementV2"] = (
                "0x0000000000000000000000000000000000000001"
            )
            target.write_text(json.dumps(payload, indent=2) + "\n")
            result = run(str(GATE))
        self.assertNotEqual(result.returncode, 0)
        self.assertIn("V2 integration manifest drift", result.stderr)

    def test_missing_pinned_runtime_hash_evidence_is_rejected(self) -> None:
        target = ROOT / "ci/manifests/v2-bytecode-manifest.json"
        with restored(target):
            payload = json.loads(target.read_text())
            for record in payload["records"]:
                if record["chain"] == "base" and record["name"] == "MissionEnforcement":
                    record["runtimeBytecodeSha256"] = "00" * 32
                    break
            else:
                self.fail("Base MissionEnforcement bytecode record missing from test fixture")
            target.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n")
            result = run(str(GATE))
        self.assertNotEqual(result.returncode, 0)
        self.assertIn("pinned runtime manifest drift", result.stderr)

    def test_false_readiness_is_rejected(self) -> None:
        target = ROOT / "pr8/pilot/base_v3_read_only_bootstrap_profile.json"
        with restored(target):
            payload = json.loads(target.read_text())
            payload["pilotReady"] = True
            target.write_text(json.dumps(payload, indent=2) + "\n")
            result = run(str(GATE))
        self.assertNotEqual(result.returncode, 0)
        self.assertIn("falsely claims pilotReady", result.stderr)


if __name__ == "__main__":
    unittest.main()
