from __future__ import annotations

import copy
import sys
import unittest
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 SeventhGenerationAuditRegressions(unittest.TestCase):
    def schema(self, name):
        return s.load_json(ROOT / "schemas/evidence" / name)

    def rejected(self, name, document):
        with self.assertRaises(Exception):
            s.validate(document, self.schema(name))

    def old_legal(self):
        return {
            "schemaVersion": "1.0.0",
            "recordType": "independent-legal-entity-verification",
            "authorityRootId": "trust-root:" + "1" * 64,
            "issuerClass": "government-registry",
            "issuerName": "Self Declared Registry",
            "organizationId": "organization:" + "2" * 64,
            "legalName": "Example Incorporated",
            "jurisdiction": "Example Jurisdiction",
            "registrationReference": "EXAMPLE-123",
            "entityStatus": "active",
            "verifiedAtUtc": "2026-08-20T00:00:00Z",
            "expiresAtUtc": "2027-08-20T00:00:00Z",
            "registryResponseSha256": "3" * 64,
            "signedPayloadSha256": "4" * 64,
            "signatureBase64": "A" * 86 + "==",
        }

    def old_exercise(self, start="1970-01-01T00:00:00Z", end="1970-01-01T01:00:00Z"):
        return {
            "schemaVersion": "1.0.0",
            "recordType": "incident-response-exercise",
            "candidateId": "sha256:" + "c" * 64,
            "sourceCommit": "a" * 40,
            "gitTree": "b" * 40,
            "scopeSha256": "d" * 64,
            "exerciseId": "incident-exercise:" + "8" * 64,
            "scenarioClass": "credential-compromise",
            "startedAtUtc": start,
            "endedAtUtc": end,
            "participantRoles": [
                "incident-commander", "security-contact",
                "partner-authorized-signatory",
            ],
            "channelsExercised": ["channel:" + "8" * 64, "channel:" + "9" * 64],
            "objectives": [
                {
                    "objectiveId": objective,
                    "assertionCount": 1,
                    "passed": True,
                    "evidenceLogSha256": str(index + 1) * 64,
                }
                for index, objective in enumerate(["detect", "triage", "escalate"])
            ],
            "failures": [],
            "correctiveActions": [],
            "allPassed": True,
        }

    def old_unlinkability(self):
        tests = [
            {
                "testId": test_id,
                "methodId": "statistical-linkage-challenge-v1",
                "sampleCount": 400,
                "expected": "unlinkable",
                "observed": "unlinkable",
                "passed": True,
                "logPath": f"pr10/partner-package/evidence/organizational/privacy-logs/t{i}.json",
                "logSha256": str(i + 1) * 64,
            }
            for i, test_id in enumerate([
                "privacy.commitment-reuse",
                "privacy.cross-partner-collusion",
                "privacy.metadata-timing-correlation",
            ])
        ]
        return {
            "schemaVersion": "2.0.0",
            "recordType": "privacy-unlinkability-test-results",
            "candidateId": "sha256:" + "c" * 64,
            "sourceCommit": "a" * 40,
            "gitTree": "b" * 40,
            "scopeSha256": "d" * 64,
            "chain": "base",
            "threatModel": {
                "modelId": "vaultfire-public-observer-collusion-v1",
                "prohibitsCrossPartnerLinkage": True,
                "prohibitsCrossChainIdentityLinkage": True,
                "attackerCapabilities": [
                    "cross-partner-collusion", "public-chain-observation",
                    "timing-correlation",
                ],
                "linkageVectors": [
                    "commitment-reuse", "identifier-reuse",
                    "metadata-correlation", "timing-correlation",
                ],
            },
            "methodology": {
                "methodologyId": "vaultfire-chain-specific-unlinkability-v1",
                "implementationVersion": "1.0.0",
                "sampleCount": 1200,
                "positivePairCount": 100,
                "negativePairCount": 100,
                "confidenceLevelBps": 9500,
                "maximumFalseLinkRateBps": 100,
                "observedFalseLinkRateBps": 0,
                "randomSeedCommitmentSha256": "a" * 64,
                "corpusSha256": "b" * 64,
                "commandSha256": "c" * 64,
                "usesRawPersonalData": False,
                "usesSurveillanceInputs": False,
            },
            "privacyPredicates": {
                "explicitConsent": True, "dataMinimized": True,
                "chainSpecific": True, "nonLinkable": True,
                "crossChainValueFlowAbsent": True,
            },
            "allPassed": True,
            "summary": {
                "total": 3, "passed": 3, "failed": 0, "totalSamples": 1200,
                "combinedLogPath": "pr10/partner-package/evidence/organizational/privacy-logs/combined.json",
                "combinedLogSha256": "f" * 64,
            },
            "tests": tests,
        }

    def old_contact(self):
        return {
            "schemaVersion": "1.0.0",
            "recordType": "contact-reachability-test",
            "candidateId": "sha256:" + "c" * 64,
            "scopeSha256": "d" * 64,
            "function": "incident",
            "roleId": "incident-commander",
            "identityId": "identity:" + "1" * 64,
            "organizationId": "organization:" + "2" * 64,
            "channelId": "channel:" + "3" * 64,
            "channelType": "paging-route",
            "testedAtUtc": "2026-08-20T00:00:00Z",
            "deliveryConfirmed": True,
            "acknowledgmentConfirmed": True,
            "acknowledgmentSeconds": 0,
            "receiptSha256": "4" * 64,
        }

    def old_liability(self):
        return {
            "schemaVersion": "2.0.0",
            "recordType": "bounded-liability-decision",
            "candidateId": "sha256:" + "c" * 64,
            "subjectOrganizationId": "organization:" + "1" * 64,
            "partnerOrganizationId": "organization:" + "2" * 64,
            "currency": "USD", "amountUnit": "usd-cents",
            "perEventCap": "100", "aggregateCap": "100",
            "maximumLoss": "100", "reserveAmount": "100",
            "maximumCoveredEvents": 1,
            "remedies": [
                {"remedyType": "refund", "description": "A complete refund remedy applies.", "maximumAmount": "100"},
                {"remedyType": "service-credit", "description": "A complete service credit applies.", "maximumAmount": "100"},
            ],
            "residualLiability": {
                "allocationBasis": "aggregate-cap-controls-except-enumerated-categories",
                "description": "The listed category remains outside the asserted cap.",
                "uncappedCategories": ["fraud"],
            },
            "acceptedBy": [],
        }

    def test_seventh_01_legal_same_human_root_self_declares_government_registry(self):
        self.rejected("legal-entity-verification.schema.json", self.old_legal())

    def test_seventh_02_legal_unresolved_registry_response_digest(self):
        document = self.old_legal()
        document["authorityRootId"] = "legal-registry-root:" + "1" * 64
        document["issuerId"] = "legal-registry-issuer:" + "2" * 64
        self.rejected("legal-entity-verification.schema.json", document)

    def test_seventh_03_incident_ancient_exercise_no_freshness(self):
        self.rejected("incident-exercise.schema.json", self.old_exercise())

    def test_seventh_04_incident_future_exercise_no_causal_bound(self):
        self.rejected("incident-exercise.schema.json", self.old_exercise("2099-01-01T00:00:00Z", "2099-01-01T01:00:00Z"))

    def test_seventh_05_incident_arbitrary_unbound_channel_ids(self):
        self.rejected("incident-exercise.schema.json", self.old_exercise())

    def test_seventh_06_incident_participant_roles_without_identity_or_org(self):
        self.rejected("incident-exercise.schema.json", self.old_exercise())

    def test_seventh_07_incident_unresolved_objective_log_digests(self):
        self.rejected("incident-exercise.schema.json", self.old_exercise())

    def test_seventh_08_unlink_methodology_observed_rate_not_derived_from_logs(self):
        self.rejected("unlinkability-test-results.schema.json", self.old_unlinkability())
        observed, upper = c.unlinkability_upper_bound_bps(12, 1200, 9500)
        self.assertEqual(observed, 100)
        self.assertGreater(upper, observed)

    def test_seventh_09_unlink_combined_log_does_not_equal_component_arithmetic(self):
        self.rejected("unlinkability-test-results.schema.json", self.old_unlinkability())

    def test_seventh_10_unlink_transcript_digests_never_resolved(self):
        log_schema = self.schema("privacy-unlinkability-log.schema.json")
        self.assertIn("transcriptPath", log_schema["required"])
        self.assertIn("transcriptSha256", log_schema["required"])

    def test_seventh_11_unlink_confidence_level_declared_without_calculation(self):
        observed, upper = c.unlinkability_upper_bound_bps(0, 100000, 9500)
        self.assertEqual(observed, 0)
        self.assertGreater(upper, 0)
        self.assertEqual((observed, upper), c.unlinkability_upper_bound_bps(0, 100000, 9500))

    def test_seventh_12_contact_receipt_digest_not_resolved(self):
        self.rejected("contact-reachability-test.schema.json", self.old_contact())

    def test_seventh_13_contact_reachability_no_independent_signed_responder_receipt(self):
        receipt_schema = self.schema("contact-provider-receipt.schema.json")
        self.assertIn("signedPayloadSha256", receipt_schema["required"])
        self.assertIn("signatureBase64", receipt_schema["required"])
        self.assertEqual(receipt_schema["properties"]["signerClass"]["const"], "assigned-channel-controller")

    def test_seventh_14_liability_remedy_maxima_sum_exceeds_aggregate(self):
        self.rejected("bounded-liabilities.schema.json", self.old_liability())
        self.assertGreater(sum([100, 100]), 100)

    def test_seventh_15_liability_maximum_loss_coexists_with_uncapped_category(self):
        self.rejected("bounded-liabilities.schema.json", self.old_liability())
        schema = self.schema("bounded-liabilities.schema.json")
        residual = schema["properties"]["residualLiability"]["properties"]
        self.assertEqual(residual["uncappedCategories"]["const"], [])


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