"""Synthetic application-contract lab. No network, model, SDK or external writes.
Not a complete MCP server, schema validator or conformance suite.
Run: python3 parcelops_contract_lab.py
"""
import copy
import hashlib
import json
import re
import unittest

VERSION = "2026-07-28"
RECORDS = {"INC-104": {"status": "delayed", "revision": 7}}

def lookup(incident_id):
    if not isinstance(incident_id, str) or not re.fullmatch(r"INC-[0-9]{1,20}", incident_id):
        raise ValueError("Invalid incident ID")
    record = RECORDS.get(incident_id)
    return {"id": incident_id, "found": record is not None, "record": copy.deepcopy(record)}

def validate_request_fixture(request):
    """Validate only the explicitly modelled request fields, not all MCP rules."""
    if request.get("jsonrpc") != "2.0":
        raise ValueError("Wrong JSON-RPC version")
    if type(request.get("id")) not in (int, str):
        raise ValueError("This fixture requires an integer or string request ID")
    if request.get("method") not in {"server/discover", "tools/list", "tools/call"}:
        raise ValueError("Method outside this fixture subset")
    meta = request.get("params", {}).get("_meta", {})
    if meta.get("io.modelcontextprotocol/protocolVersion") != VERSION:
        raise ValueError("Unsupported fixture protocol version")
    if not isinstance(meta.get("io.modelcontextprotocol/clientCapabilities"), dict):
        raise ValueError("Missing client capabilities")
    return True

def intent_digest(intent):
    return hashlib.sha256(json.dumps(intent, sort_keys=True, separators=(",", ":")).encode()).hexdigest()

def approval_allows(approval, intent, principal, revision, now):
    """Trusted in-memory approval fixture; no persistence or authenticated UI."""
    return (approval["principal"] == principal and approval["intent"] == intent_digest(intent)
            and approval["revision"] == revision and now < approval["expires"])

def private_cache_key(method, params, auth_context):
    return (method, json.dumps(params, sort_keys=True, separators=(",", ":")), auth_context)

def cache_fresh(received, ttl, now):
    return ttl >= 0 and now < received + ttl

def request_fixture():
    return {"jsonrpc": "2.0", "id": 1, "method": "server/discover", "params": {"_meta": {
        "io.modelcontextprotocol/protocolVersion": VERSION,
        "io.modelcontextprotocol/clientCapabilities": {}}}}

class ContractTests(unittest.TestCase):
    def setUp(self):
        self.intent = {"operation": "append_note", "target": "INC-104", "text": "Synthetic note"}
        self.approval = {"principal": "reader-a", "intent": intent_digest(self.intent), "revision": 7, "expires": 100}
    def test_existing(self): self.assertEqual(lookup("INC-104")["record"]["revision"], 7)
    def test_missing(self): self.assertFalse(lookup("INC-999")["found"])
    def test_malformed(self):
        for value in ["../secret", "INC-", "INC-1; shell", 104, "INC-" + "1" * 21]:
            with self.subTest(value=value), self.assertRaises(ValueError): lookup(value)
    def test_result_copy(self):
        result = lookup("INC-104"); result["record"]["status"] = "changed"
        self.assertEqual(lookup("INC-104")["record"]["status"], "delayed")
    def test_valid_request(self): self.assertTrue(validate_request_fixture(request_fixture()))
    def test_missing_capabilities(self):
        value = request_fixture(); del value["params"]["_meta"]["io.modelcontextprotocol/clientCapabilities"]
        with self.assertRaises(ValueError): validate_request_fixture(value)
    def test_wrong_era(self):
        value = request_fixture(); value["params"]["_meta"]["io.modelcontextprotocol/protocolVersion"] = "2025-11-25"
        with self.assertRaises(ValueError): validate_request_fixture(value)
    def test_boolean_id_rejected(self):
        value = request_fixture(); value["id"] = True
        with self.assertRaises(ValueError): validate_request_fixture(value)
    def test_approved_intent(self): self.assertTrue(approval_allows(self.approval, self.intent, "reader-a", 7, 99))
    def test_changed_payload(self):
        value = dict(self.intent, text="Different note")
        self.assertFalse(approval_allows(self.approval, value, "reader-a", 7, 99))
    def test_changed_principal(self): self.assertFalse(approval_allows(self.approval, self.intent, "reader-b", 7, 99))
    def test_stale_revision(self): self.assertFalse(approval_allows(self.approval, self.intent, "reader-a", 8, 99))
    def test_expired(self): self.assertFalse(approval_allows(self.approval, self.intent, "reader-a", 7, 100))
    def test_private_cache(self):
        params = {"uri": "parcelops://incidents/INC-104"}
        self.assertNotEqual(private_cache_key("resources/read", params, "a"), private_cache_key("resources/read", params, "b"))
    def test_parameter_cache(self): self.assertNotEqual(private_cache_key("resources/read", {"uri": "a"}, "x"), private_cache_key("resources/read", {"uri": "b"}, "x"))
    def test_expiry_boundary(self):
        self.assertTrue(cache_fresh(1000, 5000, 5999)); self.assertFalse(cache_fresh(1000, 5000, 6000))

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