from __future__ import annotations
REQUIRED=['deck_id', 'lesson_outcome_ref', 'slide_id', 'slide_purpose', 'claim_ids', 'content_model_ref', 'visual_type', 'visual_semantics', 'alt_or_description', 'speaker_note_ref', 'layout_template', 'reading_order', 'text_density', 'contrast_state', 'export_format', 'export_accessibility_state', 'review_state', 'revision', 'provenance']
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 not x.get('slide_purpose') or not x.get('lesson_outcome_ref') or not x.get('claim_ids'):return {'state':'HOLD_SLIDE_PURPOSE','reason':'PURPOSE_AND_CLAIM_TRACE_REQUIRED'}
    prov=x.get('provenance') or {}
    if not prov.get('fixture') or not prov.get('rule_version'):return {'state':'HOLD_PROVENANCE','reason':'PROVENANCE_REQUIRED'}
    if x.get('export_accessibility_state')=='FAIL_READING_ORDER_AND_ALT':return {'state':'HOLD_EXPORT_A11Y_REPAIR_REQUIRED','reason':'MULTIPLE_EXPORT_A11Y_FAILURES'}
    if not x.get('visual_semantics') or x.get('visual_semantics')=='MISSING':return {'state':'HOLD_VISUAL_SEMANTICS','reason':'INSTRUCTIONAL_VISUAL_SEMANTICS_REQUIRED'}
    if x.get('visual_type') not in {'NONE','DECORATIVE'} and x.get('alt_or_description') in {None,'','MISSING'}:return {'state':'HOLD_ALT_DESCRIPTION','reason':'ALTERNATIVE_REQUIRED'}
    if x.get('delivery_mode')=='PRESENTER_LED' and x.get('text_density')=='LOW' and x.get('speaker_note_ref') in {None,'','MISSING'}:return {'state':'HOLD_SPEAKER_NOTE_DEPENDENCY','reason':'SPARSE_SLIDE_LOST_REQUIRED_NARRATION'}
    if x.get('reading_order') not in {'PASS','SOURCE_PASS_EXPORT_PASS'}:return {'state':'HOLD_READING_ORDER','reason':'EXPORT_READING_ORDER_INVALID'}
    if str(x.get('text_density','')).startswith('HIGH'):return {'state':'HOLD_TEXT_DENSITY_REVIEW','reason':'DENSITY_REVIEW_REQUIRED'}
    if x.get('contrast_state')!='PASS':return {'state':'HOLD_CONTRAST','reason':'CONTRAST_NOT_PASS'}
    if x.get('export_accessibility_state')!='PASS':return {'state':'HOLD_EXPORT_A11Y_REPAIR_REQUIRED','reason':'EXPORT_ACCESSIBILITY_NOT_PASS'}
    rs=str(x.get('review_state',''))
    rev=str(x.get('revision',''))
    if rs.startswith('APPROVED_FOR_') and rs.split('APPROVED_FOR_',1)[1]!=rev:return {'state':'HOLD_STALE_REVIEW','reason':'REVIEW_REVISION_MISMATCH'}
    if x.get('delivery_mode')=='SELF_PACED_HTML':
        if x.get('speaker_note_ref')!='MOVED_TO_VISIBLE_AND_ASSISTIVE':return {'state':'HOLD_SPEAKER_NOTE_DEPENDENCY','reason':'SELF_PACED_REQUIRES_VISIBLE_OR_ASSISTIVE_MEANING'}
        return {'state':'SELF_PACED_REVIEW_REQUIRED','reason':'TRANSFER_RECOMPUTED'}
    return {'state':'VISUAL_PRODUCTION_REVIEW_REQUIRED','reason':'BOUNDED_REVIEW_READY'}

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