import math
ALLOWED={'USABLE','STALE','INSUFFICIENT','HOLD'}

def nearest_rank(values,p):
    xs=sorted(float(x) for x in values)
    if not xs: return None
    rank=max(1,math.ceil((p/100.0)*len(xs)))
    return xs[rank-1]

def evaluate(c):
    values=c.get('observations') or []
    p50=nearest_rank(values,50); p80=nearest_rank(values,80); p95=nearest_rank(values,95)
    missing=[k for k in ['forecast_id','forecast_object','horizon','sample_window','scenario','distribution_method','unit','tail_driver','assumption_set','decision_use','evidence_ref'] if c.get(k) in (None,'',[])]
    fresh=bool(c.get('data_freshness_days') is not None and c['data_freshness_days']<=c.get('max_freshness_days',14))
    complete=bool(c.get('sample_complete',False))
    horizon_fit=bool(c.get('horizon_fitted',False))
    known_events_included=bool(c.get('known_events_included',False))
    backtest=c.get('backtest_error')
    state='USABLE'; reason='PASS'
    if missing or not complete or not horizon_fit or not known_events_included:
        state='HOLD'; reason='EVIDENCE_OR_SCENARIO_REBUILD_REQUIRED'
    elif not fresh:
        state='STALE'; reason='DATA_STALE'
    elif backtest is None or backtest>0.15 or len(values)<20:
        state='INSUFFICIENT'; reason='FORECAST_EVIDENCE_INSUFFICIENT'
    return {'decision_state':state,'reason_code':reason,'sample_size':len(values),'p50':p50,'p80':p80,'p95':p95,'data_fresh':fresh,'sample_complete':complete,'horizon_fitted':horizon_fit,'known_events_included':known_events_included,'missing_fields':missing,'evidence_ref':c.get('evidence_ref')}
