import copy, json, pathlib, sys
from jsonschema import Draft202012Validator, FormatChecker
from referencing import Registry, Resource

root=pathlib.Path(sys.argv[1])
load=lambda n: json.loads((root/n).read_text())
case_schema=load('case.schema.json'); suite_schema=load('suite.schema.json'); manifest_schema=load('manifest.schema.json')
cases=load('cases.json'); manifest=load('manifest.json')
resources={s['$id']:Resource.from_contents(s) for s in (case_schema,suite_schema,manifest_schema)}
reg=Registry().with_resources(resources.items())
fmt=FormatChecker()
def errors(schema,obj): return sorted(Draft202012Validator(schema,registry=reg,format_checker=fmt).iter_errors(obj),key=lambda e:list(e.path))
def suite_ok(obj): return not errors(suite_schema,obj)
def manifest_ok(obj): return not errors(manifest_schema,obj)
ids=[c.get('caseId') for c in cases]
closure={
 'case_count_88':len(cases)==88,
 'ids_unique_88':len(ids)==88 and len(set(ids))==88,
 'manifest_count':manifest.get('caseCount')==len(cases),
 'ordered_ids':manifest.get('orderedCaseIds')==ids,
 'first_last':manifest.get('firstCaseId')==ids[0] and manifest.get('lastCaseId')==ids[-1],
 'prefix_count':len(suite_schema.get('prefixItems',[]))==len(cases),
 'all_cases_schema_valid':all(not errors(case_schema,c) for c in cases),
 'all_prefix_consts_close':all(suite_schema['prefixItems'][i]['allOf'][1]['const']==cases[i] for i in range(len(cases))),
 'refs_resolve':all(ref in ('case.schema.json',) for p in suite_schema['prefixItems'] for ref in [p['allOf'][0].get('$ref')]),
}
baseline=suite_ok(cases) and manifest_ok(manifest) and all(closure.values())
idx={c['caseId']:i for i,c in enumerate(cases)}
def cm(name,cid,fn):
 x=copy.deepcopy(cases); fn(x[idx[cid]]); return name, not suite_ok(x)
def mm(name,fn):
 x=copy.deepcopy(manifest); fn(x); return name, not manifest_ok(x)
mut=[]
mut += [cm('operation_discriminator','SL-017',lambda c:c.__setitem__('operation','evaluate_expiry'))]
mut += [cm('ordered_effects','SL-078',lambda c:c['expected']['mutations'].reverse())]
mut += [cm('ownership_precedence','SL-037',lambda c:c['expected'].__setitem__('httpStatus',410))]
mut += [cm('source_accepted_plus_48h','SL-017',lambda c:c['expected']['state'].__setitem__('expiresAt','2026-01-17T11:59:59.999Z'))]
mut += [cm('zip_completed_plus_48h','SL-018',lambda c:c['expected']['state'].__setitem__('expiresAt','2028-03-01T23:29:59.999Z'))]
mut += [cm('boundary_equality_source','SL-024',lambda c:c['expected']['state'].__setitem__('expired',False))]
mut += [cm('boundary_equality_zip','SL-027',lambda c:c['expected']['state'].__setitem__('expired',False))]
mut += [cm('delivery_zip_only','SL-047',lambda c:c['input']['representations'].__setitem__(0,{'mediaType':'text/html','kind':'html'}))]
mut += [cm('private_bucket','SL-052',lambda c:c['input'].__setitem__('anonymousGetAllowed',True))]
mut += [cm('archive_traversal','SL-063',lambda c:c['input']['entries'][-1].__setitem__('path','assets/x'))]
mut += [cm('archive_symlink','SL-067',lambda c:c['input']['entries'][-1].__setitem__('type','file'))]
mut += [cm('archive_device','SL-070',lambda c:c['input']['entries'][-1].__setitem__('type','file'))]
mut += [cm('deletion_reconciliation','SL-080',lambda c:c['expected']['state'].__setitem__('retry',True))]
mut += [cm('race_lifecycle_first','SL-078',lambda c:c['input']['events'].reverse())]
mut += [cm('race_application_first','SL-079',lambda c:c['input']['events'].reverse())]
mut += [cm('slo_lag_evidence','SL-087',lambda c:c['expected']['state'].__setitem__('lagSeconds',959))]
mut += [cm('slo_readiness_boundary','SL-088',lambda c:c['expected']['state'].__setitem__('readiness','READY'))]
mut += [mm('manifest_duplicate_id',lambda m:m['orderedCaseIds'].__setitem__(1,m['orderedCaseIds'][0]))]
mut += [mm('manifest_count_disagreement',lambda m:m.__setitem__('caseCount',87))]
mut += [mm('manifest_order',lambda m:m['orderedCaseIds'].reverse())]
result={'baseline':baseline,'closure':closure,'mutations':dict(mut),'all_mutations_rejected':all(v for _,v in mut),'mutation_count':len(mut)}
print(json.dumps(result,sort_keys=True))
sys.exit(0 if baseline and result['all_mutations_rejected'] else 1)
