import pathlib,subprocess,sys,os,json,copy
LAB=pathlib.Path(__file__).resolve().parents[1];sys.path.insert(0,str(LAB/'src'))
from lifecycle_policy import *
q=subprocess.run([sys.executable,str(LAB/'test/public-contract.test_q1.py')],capture_output=True,text=True,env={**os.environ,'PYTHONDONTWRITEBYTECODE':'1'})
checks=[]
for line in q.stdout.splitlines():
    if line.startswith('PASS '): checks.append(('q1_'+line[5:].strip(),True))
    if line.startswith('FAIL '): checks.append(('q1_'+line[5:].strip(),False))
def c(n,x): checks.append((n,bool(x)))
tool=json.loads((LAB/'toolchain-policy.json').read_text()); req=json.loads((LAB/'requirements.json').read_text()); pol=json.loads((LAB/'fixture/golden-policy.json').read_text()); ev=json.loads((LAB/'evidence/Data_Lifecycle_Policy_Evidence_Record_v1.template.json').read_text()); tr=json.loads((LAB/'transfer/changed-context.template.json').read_text()); dec=json.loads((LAB/'submission/decision.template.json').read_text()); val=json.loads((LAB/'evidence/validation-status.json').read_text())
c('q2_local_only',tool.get('local_only') is True)
c('q2_network_forbidden',tool.get('network_forbidden') is True)
c('q2_external_legal_forbidden',tool.get('external_legal_service_forbidden') is True)
c('q2_irreversible_forbidden',tool.get('irreversible_side_effects_forbidden') is True)
c('q2_runtime_python_only',tool.get('allowed_runtime')==['python_stdlib'])
c('q2_requirements_negative',req.get('must_preserve_negative_evidence') is True)
c('q2_requirements_transfer',req.get('changed_context_transfer_required') is True)
c('q2_requirements_owner_blank',req.get('owner_acceptance_must_be_blank') is True)
c('q2_requirements_pii_forbidden',req.get('real_personal_data_forbidden') is True)
c('q2_requirements_legal_advice_forbidden',req.get('jurisdiction_specific_legal_advice_forbidden') is True)
c('q2_requirements_universal_period_forbidden',req.get('universal_retention_period_forbidden') is True)
c('q2_evidence_starter_blank',ev.get('rows')==[] and ev.get('positive_evidence')==[] and ev.get('negative_evidence')==[] and ev.get('owner_acceptance')=='' and ev.get('changed_context_transfer',{}).get('final_decision')=='')
c('q2_policy_lifecycle_boundary',pol.get('production_proof') is False and pol.get('jurisdiction_specific_law') is False and all(v.get('hold_allowed') is True for v in pol.get('classes',{}).values()) and pol.get('deletion',{}).get('owned_copy_states_required')==['primary_disposed','derived_disposed'])
records=json.loads((LAB/'fixture/baseline-data.json').read_text())['records'];op=records[0]
good={'primary_disposed':True,'derived_disposed':True,'backup_boundary':'DOCUMENTED_EXPIRY_BOUNDARY','retention_exit_satisfied':False}
c('runtime_retention_exit_deny',evaluate_deletion(op,good,None,pol)['decision']=='DENY_RETENTION_EXIT_NOT_SATISFIED')
p2=copy.deepcopy(pol);p2['classes']['operational_profile']['hold_allowed']=False
c('runtime_hold_permission_deny',evaluate_deletion(op,{'primary_disposed':True,'derived_disposed':True,'backup_boundary':'DOCUMENTED_EXPIRY_BOUNDARY'},{'active':True,'reason':'x','owner':'gov'},p2)['decision']=='DENY_HOLD_NOT_ALLOWED')
ret=retention_decision(op,pol);exp=evaluate_export({'record_id':op['record_id'],'requester_role':'role:support-exporter','processing_purpose':op['processing_purpose'],'requested_fields':['record_id']},records,pol);dele=evaluate_deletion(op,{'primary_disposed':True,'derived_disposed':True,'backup_boundary':'DOCUMENTED_EXPIRY_BOUNDARY'},None,pol);row=build_evidence_row(op,ret,exp,dele);row['extra_unapproved']='x'
c('runtime_extra_evidence_rejected',evidence_row_complete(row) is False)
for n,ok in checks: print(('PASS' if ok else 'FAIL'),n)
print(f'SUMMARY {sum(x[1] for x in checks)}/{len(checks)} PASS')
raise SystemExit(0 if q.returncode==0 and all(x[1] for x in checks) else 1)
