from __future__ import annotations
REQUIRED=['experience_id','media_type','content_purpose','caption_state','transcript_state','audio_description_state','text_alternative_state','keyboard_path','focus_order_state','contrast_check','resize_reflow_state','pause_stop_control','error_or_barrier','wcag_ref','severity','remediation_owner','verification_state']
UNKNOWN={'UNKNOWN','STALE','INCONCLUSIVE','DRIFTED','UNVERIFIED','MISSING',''}
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_ORACLE_REUSE_FORBIDDEN'}
    if any(str(x.get(k,'')).upper() in UNKNOWN for k in ['caption_state','transcript_state','keyboard_path','focus_order_state','contrast_check','resize_reflow_state','pause_stop_control']):
        return {'state':'HOLD_DECISIVE_EVIDENCE_UNKNOWN','reason':'DECISIVE_STATE_UNKNOWN_OR_STALE'}
    # A baseline with multiple simultaneous critical barriers reports the aggregate state first.
    # Single-failure mutation fixtures keep their more specific diagnostic states.
    if int(x.get('critical_barrier_count',0) or 0) > 1:
        return {'state':'HOLD_CRITICAL_ACCESS_BARRIERS','reason':'MULTIPLE_CRITICAL_ACCESS_BARRIERS'}
    if str(x.get('caption_state')).startswith('INCOMPLETE'):
        return {'state':'HOLD_CAPTION_COMPLETENESS','reason':'REQUIRED_CAPTION_INFORMATION_MISSING'}
    if str(x.get('text_alternative_state')).startswith('MISSING'):
        return {'state':'HOLD_TEXT_ALTERNATIVE','reason':'INSTRUCTIONAL_VISUAL_MEANING_UNAVAILABLE'}
    if str(x.get('audio_description_state')).startswith('MISSING') and x.get('media_type') in {'SYNCHRONIZED_VIDEO','VIDEO'}:
        return {'state':'HOLD_VISUAL_DESCRIPTION','reason':'REQUIRED_VISUAL_INFORMATION_NOT_AVAILABLE'}
    if str(x.get('keyboard_path')).startswith('FAIL'):
        return {'state':'HOLD_KEYBOARD_PATH','reason':'KEYBOARD_PATH_NOT_OPERABLE'}
    if str(x.get('focus_order_state')).startswith('FAIL'):
        return {'state':'HOLD_FOCUS_PATH','reason':'FOCUS_ORDER_OR_RESTORE_INVALID'}
    if str(x.get('contrast_check')).startswith('FAIL'):
        return {'state':'HOLD_CONTRAST','reason':'CONTRAST_NOT_PASS'}
    if str(x.get('resize_reflow_state')).startswith('FAIL'):
        return {'state':'HOLD_REFLOW','reason':'REFLOW_OR_RESIZE_NOT_PASS'}
    if str(x.get('pause_stop_control')).startswith('FAIL'):
        return {'state':'HOLD_MEDIA_CONTROL','reason':'PAUSE_STOP_CONTROL_NOT_PASS'}
    sev=str(x.get('severity','')).upper()
    owner=str(x.get('remediation_owner','')).strip().lower()
    if sev in {'CRITICAL','HIGH'} and owner in {'','none','unassigned','missing'}:
        return {'state':'HOLD_REMEDIATION_OWNER','reason':'HIGH_SEVERITY_BARRIER_REQUIRES_OWNER'}
    if x.get('exception_requested'):
        if not x.get('exception_approved') or not x.get('exception_reason') or owner in {'','none','unassigned','missing'}:
            return {'state':'HOLD_EXCEPTION_GOVERNANCE','reason':'EXCEPTION_REQUIRES_APPROVAL_REASON_OWNER'}
    if x.get('media_type')=='AUDIO_FIRST_WITH_TRANSCRIPT' and x.get('viewport')=='NARROW_200_PERCENT':
        if x.get('resize_reflow_state')!='PASS_200_PERCENT' or not str(x.get('keyboard_path')).startswith('PASS') or not str(x.get('focus_order_state')).startswith('PASS'):
            return {'state':'HOLD_TRANSFER_RECOMPUTE','reason':'MOBILE_AUDIO_FIRST_EVIDENCE_NOT_CLOSED'}
        return {'state':'MULTIMEDIA_ACCESS_REVIEW_REQUIRED','reason':'TRANSFER_RECOMPUTED_BOUNDED_REVIEW'}
    # A single residual critical marker is still a bounded hold when no specific gate above fired.
    if int(x.get('critical_barrier_count',0) or 0) == 1:
        return {'state':'HOLD_CRITICAL_ACCESS_BARRIERS','reason':'ONE_CRITICAL_ACCESS_BARRIER'}
    return {'state':'MULTIMEDIA_ACCESS_REVIEW_REQUIRED','reason':'BOUNDED_REVIEW_READY'}

def inspect_transfer(x):
    r=evaluate(x)
    return {'state':r['state'],'reason':r['reason'],'media_type':x.get('media_type'),'viewport':x.get('viewport'),'baseline_oracle_reuse':x.get('baseline_oracle_reuse')}
