import argparse,json,re
from pathlib import Path
MAP={"ALIGNED_MOCK":("NONE","SYNTHETIC_MOCK_COMPLETED","ALL_FOUR_ALIGNED","REPEAT_WITH_AUTHORIZED_REAL_DATA"),"MONITOR_LAG":("MONITOR_TIMING","MEDIA_LAG_ONLY_ROOT_CAUSE_UNKNOWN","INPUT_MONITOR_TIME","CAPTURE_SYNCED_REAL_DIAGNOSTICS"),"FEEDBACK_MISSING":("FEEDBACK_QUALITY","COMPLETION_UNPROVEN_STOP","FEEDBACK_TRACE_STATE","RESTORE_FEEDBACK_AND_REPLAY"),"CROSS_SESSION":("SESSION_BINDING","EVIDENCE_SET_INVALID","INPUT_MONITOR_TIME","RECOLLECT_ONE_SESSION")}
TRACE_MAP={"VALID":("NONE","CHAIN_VALID_PROVENANCE_UNVERIFIED","CHAIN_CONTINUITY_ONLY","VERIFY_SIGNED_PROVENANCE"),"BODY_EDIT":("EVENT_HASH_MISMATCH","CHAIN_BREAK_STOP","EVENT_HASH_MISMATCH","REPAIR_OR_RECOLLECT_TRACE"),"EVENT_REMOVED":("EVENT_COUNT_INVALID","CHAIN_BREAK_STOP","EVENT_COUNT_INVALID","REPAIR_OR_RECOLLECT_TRACE"),"REHASHED_FORGERY":("NONE","CHAIN_VALID_PROVENANCE_UNVERIFIED","CHAIN_CONTINUITY_ONLY","VERIFY_SIGNED_PROVENANCE"),"AUTHORITY_CLAIM":("AUTHORITY_ESCALATION","AUTHORITY_INVALID_STOP","AUTHORITY_ESCALATION","REMOVE_UNVERIFIED_AUTHORITY_AND_REAUDIT")};TRACE_EVENTS=["OBSERVATION_ACCEPTED","POLICY_EVALUATED","MOCK_DRIVER_COMPLETED","REPORT_CANDIDATE_BUILT"]
TOP={"schemaVersion","artifactType","authority","evidenceSource","sourceLineage","selectedCase","evidenceFinding","studentScaffold","studentNarrative","systemAppendix","authorship","device","decision"};HEX=re.compile(r"^[0-9A-F]{64}$")
def out(ok,reason):return {"valid":ok,"firstReason":reason,"studentAuthorshipProven":False,"courseCreditIssued":False,"realEvidenceEligible":False,"hardwareAuthority":0,"decision":"NO_GO"}
def verify(v):
 if not isinstance(v,dict) or set(v)!=TOP:return out(False,"TOP_LEVEL_FIELDS_INVALID")
 if v.get("schemaVersion")!="0.3.0" or v.get("artifactType")!="LOCAL_EVIDENCE_TO_REPORT_DRAFT" or v.get("authority")!="LOCAL_LEARNING_DRAFT" or v.get("decision")!="NO_GO":return out(False,"IDENTITY_INVALID")
 src=v.get("evidenceSource");case=v.get("selectedCase")
 if not isinstance(src,dict) or set(src)!={"artifactType","sha256","sourceOrigin"} or src.get("sourceOrigin")!="SYNTHETIC" or not HEX.fullmatch(str(src.get("sha256",""))):return out(False,"EVIDENCE_SOURCE_INVALID")
 lineage=v.get("sourceLineage")
 if src.get("artifactType")=="LOCAL_MULTIMODAL_REVIEW_NOTE":
  expected=MAP.get(case);finding={"firstMismatch":expected[0],"minimalConclusion":expected[1],"visitedTimepoints":[0,100,200,300,500]} if expected else None
  if not isinstance(lineage,dict) or set(lineage)!={"lineageKind","cameraRoleSourceSha256","observationRecordSha256","observationAdapterSha256","multimodalReviewSha256"} or lineage.get("lineageKind")!="CAMERA_OBSERVATION_ADAPTER_REVIEW" or lineage.get("multimodalReviewSha256")!=src.get("sha256") or any(not HEX.fullmatch(str(lineage.get(k,""))) for k in ("cameraRoleSourceSha256","observationRecordSha256","observationAdapterSha256")):return out(False,"SOURCE_LINEAGE_INVALID")
  lineage_order="CAMERA_OBSERVATION_ADAPTER_REVIEW"
 elif src.get("artifactType")=="LOCAL_TRACE_AUDIT_LEARNING_NOTE":
  expected=TRACE_MAP.get(case);finding={"firstMismatch":expected[0],"minimalConclusion":expected[1],"inspectedEvents":TRACE_EVENTS} if expected else None
  if lineage!={"lineageKind":"TRACE_AUDIT","traceAuditSha256":src.get("sha256"),"provenanceVerified":False}:return out(False,"SOURCE_LINEAGE_INVALID")
  lineage_order="TRACE_CONTENT_THEN_PROVENANCE"
 else:return out(False,"EVIDENCE_SOURCE_INVALID")
 if not finding or v.get("evidenceFinding")!=finding:return out(False,"EVIDENCE_FINDING_INVALID")
 scaffold=v.get("studentScaffold");expected_scaffold={"lineageOrder":lineage_order,"claimDiscipline":"OBSERVE_BEFORE_EXPLAIN","evidenceAnchor":expected[2],"boundaryConclusion":expected[1],"validationPlan":expected[3]}
 if scaffold!=expected_scaffold:return out(False,"STUDENT_SCAFFOLD_INVALID")
 n=v.get("studentNarrative")
 if not isinstance(n,dict) or set(n)!={"observedFact","evidenceReasoning","unsupportedClaim","nextValidation"} or any(not isinstance(x,str) or len(x.strip())<24 or len(x)>360 for x in n.values()):return out(False,"STUDENT_NARRATIVE_INVALID")
 if v.get("systemAppendix")!={"sourceAuthority":"SYNTHETIC","realMediaLoaded":False,"realEvidenceEligible":False,"hardwareAuthority":0}:return out(False,"APPENDIX_AUTHORITY_INVALID")
 if v.get("authorship")!={"studentTextIncluded":True,"systemGeneratedNarrative":False,"studentAuthorshipProven":False}:return out(False,"AUTHORSHIP_BOUNDARY_INVALID")
 if v.get("device")!={"hardwareAccessed":False,"cameraOpened":False,"pcanInitialized":False,"canFramesSent":0}:return out(False,"DEVICE_AUTHORITY_ESCALATION")
 return out(True,"LOCAL_REPORT_DRAFT_STRUCTURE_VALID")
def main(argv=None):
 p=argparse.ArgumentParser();p.add_argument("draft",type=Path);a=p.parse_args(argv)
 try:r=verify(json.loads(a.draft.read_text(encoding="utf-8")))
 except Exception:r=out(False,"JSON_INVALID")
 print(json.dumps(r,ensure_ascii=False,sort_keys=True));return 0 if r["valid"] else 2
if __name__=="__main__":raise SystemExit(main())
