CRITICAL_FIELDS = ['claim_id','claim_type','claim_scope','source_id','source_locator','source_version','retrieved_at','generation_activity','responsible_agent','derivation_relation','applicability_boundary','freshness_class','reviewer','verification_result','publication_state','evidence_ref']
PROVENANCE_FIELDS = ['source_entity','generation_activity','responsible_agent','derivation_relation']

def evaluate(c):
    missing=[k for k in CRITICAL_FIELDS if c.get(k) in (None,'',[],{})]
    evidence_complete=not missing
    provenance_complete=all(c.get(k) not in (None,'',[],{}) for k in PROVENANCE_FIELDS) and bool(c.get('provenance_original_preserved',True))
    freshness_valid=not bool(c.get('source_changed_since_review',False)) and not bool(c.get('review_due_elapsed',False))
    support_valid=bool(c.get('source_supports_claim',True)) and not bool(c.get('material_dispute',False))
    if bool(c.get('material_dispute',False)):
        state='DISPUTED'
    elif not bool(c.get('source_supports_claim',True)):
        state='REJECTED'
    elif not provenance_complete:
        state='HOLD'
    elif not evidence_complete:
        # A missing applicability boundary can preserve limited support, but not full verification.
        non_boundary_missing=[k for k in missing if k!='applicability_boundary']
        state='PARTIAL' if not non_boundary_missing and c.get('source_supports_claim',True) else 'HOLD'
    elif not freshness_valid:
        state='STALE'
    else:
        state='VERIFIED'
    publication_allowed=(state=='VERIFIED')
    return {'state':state,'evidence_complete':evidence_complete,'provenance_complete':provenance_complete,'freshness_valid':freshness_valid,'support_valid':support_valid,'publication_allowed':publication_allowed}
