"""Wraps the independent bound-gate verifier as a unittest.

Also asserts the generator is deterministic (re-generating reproduces the
committed artifact bytes) and that the committed fixture keeps its experimental,
non-production, replay-unavailable labels.
"""
import hashlib
import copy
import json
import unittest
from pathlib import Path

import verify_bound_gate

HERE = Path(__file__).resolve().parent


class BoundGateTests(unittest.TestCase):
    def setUp(self):
        self.fixture = json.loads((HERE / "bound-gate.v1.json").read_text(encoding="utf-8"))
        self.root = json.loads((HERE / "trust-root.json").read_text(encoding="utf-8"))

    def check_mutation(self, vector=None, definitions=None, failed=""):
        vector = self.fixture["valid_vector"] if vector is None else vector
        definitions = self.fixture["invariant_definitions"] if definitions is None else definitions
        checks = verify_bound_gate.evaluate(vector, self.root, definitions)
        self.assertEqual(set(verify_bound_gate.CHECKS), {k for k in checks if not k.startswith("_")})
        self.assertEqual(verify_bound_gate.gate_result(checks)[0], "HOLD")
        self.assertEqual(checks[failed][0], "FAIL")

    def test_published_definition_mutation_holds(self):
        definitions = copy.deepcopy(self.fixture["invariant_definitions"])
        definitions[1]["predicate"] = "request.evidence_tier < policy.min_evidence_tier"
        self.check_mutation(definitions=definitions, failed="definition_binding")

    def test_missing_published_definitions_holds(self):
        self.check_mutation(definitions=[], failed="definition_binding")

    def test_published_measurement_mutation_holds(self):
        vector = copy.deepcopy(self.fixture["valid_vector"])
        vector["invariant_measurement"]["measured"]["BG-I2"] = False
        self.check_mutation(vector=vector, failed="measurement_binding")

    def test_published_mask_mutation_holds(self):
        vector = copy.deepcopy(self.fixture["valid_vector"])
        vector["invariant_measurement"]["measured_mask_hex"] = "00"
        self.check_mutation(vector=vector, failed="measurement_binding")

    def test_published_request_field_mutation_holds(self):
        vector = copy.deepcopy(self.fixture["valid_vector"])
        vector["request"]["class_id"] = "another-class"
        self.check_mutation(vector=vector, failed="byte_conformance")

    def test_published_verdict_field_mutation_holds(self):
        vector = copy.deepcopy(self.fixture["valid_vector"])
        vector["verdict"]["key_identifier"] = "different-key"
        self.check_mutation(vector=vector, failed="byte_conformance")

    def test_test_root_identifier_mutation_holds(self):
        root = copy.deepcopy(self.root)
        root["roots"][0]["key_identifier"] = "different-key"
        checks = verify_bound_gate.evaluate(self.fixture["valid_vector"], root,
                                            self.fixture["invariant_definitions"])
        self.assertEqual(checks["test_authority"][0], "FAIL")
        self.assertEqual(verify_bound_gate.gate_result(checks)[0], "HOLD")

    def test_malformed_cbor_returns_complete_hold(self):
        vector = copy.deepcopy(self.fixture["valid_vector"])
        vector["request"]["canonical_cbor_hex"] = ""
        self.check_mutation(vector=vector, failed="byte_conformance")

    def test_malformed_signature_returns_complete_hold(self):
        vector = copy.deepcopy(self.fixture["valid_vector"])
        vector["verdict"]["signature_hex"] = "not hex"
        self.check_mutation(vector=vector, failed="signature")

    def test_unapproved_material_path_returns_complete_hold(self):
        vector = copy.deepcopy(self.fixture["valid_vector"])
        vector["policy_file"] = "../../private.json"
        self.check_mutation(vector=vector, failed="byte_conformance")

    def test_verifier_passes(self):
        self.assertEqual(verify_bound_gate.main(), 0)

    def test_generator_is_deterministic(self):
        before = (HERE / "bound-gate.v1.json").read_bytes()
        before_policy = (HERE / "material" / "policy.json").read_bytes()
        import generate
        generate.main()
        after = (HERE / "bound-gate.v1.json").read_bytes()
        after_policy = (HERE / "material" / "policy.json").read_bytes()
        self.assertEqual(before, after, "generate.py is not byte-deterministic")
        self.assertEqual(before_policy, after_policy)

    def test_labels_are_honest(self):
        fx = json.loads((HERE / "bound-gate.v1.json").read_text(encoding="utf-8"))
        self.assertTrue(fx["test_authority"])
        self.assertFalse(fx["production_identity_claimed"])
        self.assertEqual(fx["decision_replay"], "DECISION_REPLAY_UNAVAILABLE")

    def test_material_content_hashes(self):
        fx = json.loads((HERE / "bound-gate.v1.json").read_text(encoding="utf-8"))
        v = fx["valid_vector"]
        pol = (HERE / v["policy_file"]).read_bytes()
        auth = (HERE / v["authority_state_file"]).read_bytes()
        self.assertEqual(hashlib.sha256(pol).hexdigest(), v["context"]["policy_hash_hex"])
        self.assertEqual(hashlib.sha256(auth).hexdigest(), v["context"]["authority_state_hash_hex"])


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