from __future__ import annotations

import base64
import sys
import unittest
from decimal import Decimal, ROUND_CEILING, localcontext
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "scripts"))
import check_organizational_readiness as c
import strict_json_schema as s


class EighthGenerationAuditRegressions(unittest.TestCase):
    def schema(self, name):
        return s.load_json(ROOT / "schemas" / "evidence" / name)

    def test_legal_human_and_registry_roots_share_identical_key(self):
        source = (ROOT / "scripts/check_organizational_readiness.py").read_text()
        self.assertIn("legal_root_public != root_public", source)
        self.assertIn("legal_root_path != root_path", source)
        self.assertIn(
            'legal_policy["expectedRootSha256"] != policy["expectedRootSha256"]',
            source,
        )

    def test_legal_arbitrary_unresolved_response_payload_digest(self):
        schema = self.schema("legal-entity-registry-response.schema.json")
        self.assertIn("rawResponsePath", schema["required"])
        self.assertIn("rawResponseSha256", schema["required"])
        raw_schema = self.schema("legal-entity-raw-registry-response.schema.json")
        self.assertFalse(raw_schema["additionalProperties"])
        source = (ROOT / "scripts/check_organizational_readiness.py").read_text()
        self.assertIn(
            'digest(raw_response_path) == response["rawResponseSha256"]',
            source,
        )
        self.assertIn('response["responseId"] == expected_response_id', source)

    def test_incident_opaque_output_digest_never_resolved(self):
        old = {
            "schemaVersion": "1.0.0",
            "recordType": "incident-exercise-objective-log",
            "candidateId": "sha256:" + "c" * 64,
            "sourceCommit": "a" * 40,
            "gitTree": "b" * 40,
            "scopeSha256": "d" * 64,
            "exerciseId": "incident-exercise:" + "8" * 64,
            "objectiveId": "detect",
            "startedAtUtc": "2026-08-20T00:00:00Z",
            "endedAtUtc": "2026-08-20T00:01:00Z",
            "participantIdentityIds": ["identity:" + str(i) * 64 for i in (1, 2, 3)],
            "channelIds": ["channel:" + "4" * 64],
            "assertionCount": 1,
            "passed": True,
            "failures": [],
            "outputSha256": "9" * 64,
        }
        with self.assertRaises(Exception):
            s.validate(old, self.schema("incident-objective-log.schema.json"))
        source = (ROOT / "scripts/check_organizational_readiness.py").read_text()
        self.assertIn('objective_log["outputSha256"]', source)
        self.assertIn("incident-assertion-evidence.schema.json", source)

    def test_unlink_hoeffding_upper_bound_rounds_down_one_bps(self):
        observed, upper = c.unlinkability_upper_bound_bps(1, 15000, 9500)
        self.assertEqual(observed, 0)
        self.assertEqual(upper, 101)
        with localcontext() as context:
            context.prec = 80
            exact = (
                Decimal(1) * Decimal(10000) / Decimal(15000)
                + Decimal(10000)
                * (
                    (Decimal(1) / Decimal("0.05")).ln()
                    / (Decimal(2) * Decimal(15000))
                ).sqrt()
            )
            expected = int(exact.to_integral_value(rounding=ROUND_CEILING))
        self.assertEqual(upper, expected)
        for pairs in (100, 999, 1000, 15000, 100000):
            previous = None
            for false_links in range(0, min(10, pairs) + 1):
                _, bound = c.unlinkability_upper_bound_bps(
                    false_links, pairs, 9500
                )
                if previous is not None:
                    self.assertGreaterEqual(bound, previous)
                previous = bound

    def test_unlink_fabricated_self_labelled_transcripts_and_trivial_inputs(self):
        old_input = {
            "schemaVersion": "1.0.0",
            "recordType": "privacy-unlinkability-methodology-input",
            "candidateId": "sha256:" + "c" * 64,
            "sourceCommit": "a" * 40,
            "gitTree": "b" * 40,
            "scopeSha256": "d" * 64,
            "chain": "base",
            "inputType": "random-seed-opening",
            "methodologyId": "vaultfire-chain-specific-unlinkability-v1",
            "inputContentBase64": base64.b64encode(bytes(32)).decode(),
            "inputContentSha256": "0" * 64,
            "itemCount": 1,
            "containsRawPersonalData": False,
            "containsSurveillanceInputs": False,
        }
        with self.assertRaises(Exception):
            s.validate(
                old_input,
                self.schema(
                    "privacy-unlinkability-methodology-input.schema.json"
                ),
            )
        transcript = self.schema(
            "privacy-unlinkability-transcript.schema.json"
        )
        self.assertIn("executionOutputPath", transcript["required"])
        event_required = transcript["properties"]["events"]["items"]["required"]
        self.assertIn("runnerOutputSha256", event_required)
        source = (ROOT / "scripts/check_organizational_readiness.py").read_text()
        for predicate in (
            "len(set(input_content)) >= 24",
            "corpus contains repeated or degenerate challenges",
            "canonical nontrivial exact-commit runner",
            "derived_events == transcript_events",
            '"launcherSignature"',
        ):
            self.assertIn(predicate, source)


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