from __future__ import annotations
REQUIRED=['credential_id', 'credential_type', 'subject_id', 'issuer_id', 'issuer_authority_ref', 'competency_id', 'criteria_ref', 'evidence_refs', 'mastery_snapshot_ref', 'issuance_decision_ref', 'issued_at', 'validity_or_review', 'credential_status_ref', 'proof_or_verification_method', 'privacy_disclosure_state', 'verification_result', 'revocation_or_update_reason']
UNKNOWN={'UNKNOWN','STALE','INCONCLUSIVE','UNVERIFIED','MISSING',''}

def empty(v):
    return v is None or (isinstance(v,str) and v.strip().upper() in UNKNOWN)

def evaluate(x):
    missing=[k for k in REQUIRED if k not in x]
    if missing:return {'state':'HOLD_INSUFFICIENT_EVIDENCE','reason':'MISSING_FIELDS','missing':missing}
    if x.get('baseline_oracle_reuse'):return {'state':'HOLD_BASELINE_ORACLE_REUSE','reason':'BASELINE_DECISION_REUSE_FORBIDDEN'}
    if empty(x.get('credential_id')) or empty(x.get('credential_type')):return {'state':'HOLD_CREDENTIAL_IDENTITY','reason':'CREDENTIAL_ID_AND_TYPE_REQUIRED'}
    if empty(x.get('subject_id')) or x.get('subject_binding_valid') is False:return {'state':'HOLD_SUBJECT_BINDING','reason':'SUBJECT_BINDING_REQUIRED'}
    if empty(x.get('issuer_id')) or empty(x.get('issuer_authority_ref')) or x.get('issuer_authority_valid') is False:return {'state':'HOLD_ISSUER_AUTHORITY','reason':'AUTHORIZED_ISSUER_REQUIRED'}
    if empty(x.get('competency_id')) or empty(x.get('criteria_ref')) or x.get('criteria_competency_match') is False:return {'state':'HOLD_COMPETENCY_CRITERIA_MISMATCH','reason':'COMPETENCY_AND_CRITERIA_MUST_MATCH'}
    refs=x.get('evidence_refs')
    if not isinstance(refs,list) or not refs or any(empty(v) for v in refs) or x.get('evidence_sufficient') is False or x.get('evidence_reference_resolves') is False:return {'state':'HOLD_EVIDENCE_REFS','reason':'SUFFICIENT_RESOLVABLE_EVIDENCE_REFERENCES_REQUIRED'}
    if x.get('underlying_mastery_rewritten'):return {'state':'HOLD_MASTERY_HISTORY_MUTATION','reason':'CREDENTIAL_LIFECYCLE_MUST_NOT_REWRITE_MASTERY_HISTORY'}
    if empty(x.get('mastery_snapshot_ref')) or str(x.get('mastery_state','')).upper() not in {'MASTERED','PROVISIONAL_MASTERED'}:return {'state':'HOLD_MASTERY_SNAPSHOT','reason':'GOVERNED_MASTERY_SNAPSHOT_REQUIRED'}
    if x.get('transfer_context_changed') and not x.get('mapping_rebuilt'):return {'state':'HOLD_MAPPING_NOT_REBUILT','reason':'CHANGED_ISSUER_OR_FORMAT_REQUIRES_NEW_MAPPING_AND_ISSUANCE_DECISION'}
    if empty(x.get('issuance_decision_ref')):return {'state':'HOLD_ISSUANCE_DECISION','reason':'SEPARATE_ISSUANCE_DECISION_REQUIRED'}
    lifecycle=str(x.get('credential_lifecycle_state','PENDING')).upper()
    issued=x.get('issued_at')
    if lifecycle in {'ACTIVE','REVOKED','SUSPENDED','UPDATED'} and (empty(issued) or str(issued).upper()=='NOT_ISSUED'):return {'state':'HOLD_ISSUED_AT','reason':'ISSUED_CREDENTIAL_REQUIRES_ISSUED_AT'}
    if empty(x.get('validity_or_review')) or empty(x.get('credential_status_ref')):return {'state':'HOLD_CREDENTIAL_STATUS','reason':'LIFECYCLE_AND_STATUS_REFERENCE_REQUIRED'}
    if x.get('status_service_ready') is False:
        return {'state':'CREDENTIAL_ISSUANCE_HOLD_STATUS_VERIFICATION','reason':'STATUS_SERVICE_NOT_READY'}
    if empty(x.get('proof_or_verification_method')) or x.get('proof_supported') is False:return {'state':'HOLD_VERIFICATION_METHOD','reason':'SUPPORTED_PROOF_OR_VERIFICATION_METHOD_REQUIRED'}
    ps=str(x.get('privacy_disclosure_state','')).upper()
    if empty(ps) or x.get('privacy_least_disclosure') is False or ps in {'FULL_SENSITIVE_EVIDENCE_EMBEDDED','OVERDISCLOSED'}:return {'state':'HOLD_PRIVACY_DISCLOSURE','reason':'PRIVACY_MINIMIZATION_REQUIRED'}
    vr=str(x.get('verification_result','')).upper()
    if vr in {'FAILED','INVALID','REJECTED'}:return {'state':'HOLD_VERIFICATION_FAILED','reason':'VERIFICATION_FAILED'}
    if vr in {'NOT_RUN','PENDING','UNKNOWN','UNVERIFIED',''}:
        return {'state':'CREDENTIAL_ISSUANCE_HOLD_STATUS_VERIFICATION','reason':'VERIFICATION_NOT_COMPLETE'}
    reason=str(x.get('revocation_or_update_reason','')).strip()
    if lifecycle=='REVOKED':
        if not reason or reason.upper() in {'NONE','MISSING','UNKNOWN'}:return {'state':'HOLD_REVOCATION_REASON','reason':'REVOKED_CREDENTIAL_REQUIRES_REASON'}
        return {'state':'CREDENTIAL_REVOKED_REVIEW_READY','reason':'REVOKED_STATUS_RECORDED_MASTERY_PRESERVED'}
    if lifecycle in {'UPDATED','SUSPENDED'}:
        if not reason or reason.upper() in {'NONE','MISSING','UNKNOWN'}:return {'state':'HOLD_REVOCATION_REASON','reason':'LIFECYCLE_UPDATE_REQUIRES_REASON'}
        return {'state':'CREDENTIAL_UPDATE_REVIEW_REQUIRED','reason':'UPDATE_OR_SUSPENSION_RECORDED'}
    return {'state':'CREDENTIAL_ISSUANCE_REVIEW_READY','reason':'IDENTITY_AUTHORITY_EVIDENCE_STATUS_PRIVACY_AND_VERIFICATION_RECONCILED'}
