from collections import defaultdict

def evaluate(c):
    targets=defaultdict(float); unallocated=0.0; total=0.0
    dispute=None
    method_override=c.get('shared_method_override')
    for item in c.get('cost_items',[]):
        amount=float(item.get('amount',0)); total+=amount
        if item.get('kind')=='DIRECT':
            target=item.get('target')
            if target:
                targets[target]+=amount
            elif c.get('force_unallocated_zero') and c.get('forced_target'):
                targets[c['forced_target']]+=amount; dispute='FORCED_ALLOCATION_WITHOUT_EVIDENCE'
            else:
                unallocated+=amount
        elif item.get('kind')=='SHARED':
            quantities=item.get('quantities') or {}
            method=method_override or c.get('approved_shared_method')
            if method=='EQUAL_SPLIT':
                keys=sorted(quantities)
                if not keys: unallocated+=amount; continue
                each=amount/len(keys)
                for k in keys: targets[k]+=each
                if c.get('approved_shared_method')!='EQUAL_SPLIT': dispute='UNAPPROVED_EQUAL_SPLIT'
            elif method=='USAGE_PROPORTIONAL':
                denom=sum(float(v) for v in quantities.values())
                if denom<=0: unallocated+=amount; continue
                for k,v in quantities.items(): targets[k]+=amount*float(v)/denom
            else:
                unallocated+=amount
    alloc=round(sum(targets.values()),2); unalloc=round(unallocated,2); total=round(total,2)
    rounded={k:round(v,2) for k,v in sorted(targets.items())}
    conserve=abs((alloc+unalloc)-total)<0.011
    if c.get('owner_blocked'):
        state,reason='BLOCKED','CHARGEBACK_AUTHORITY_BLOCK'
    elif c.get('evidence_age_days',0)>30:
        state,reason='STALE','DECISIVE_EVIDENCE_STALE'
    elif c.get('material_change_not_recomputed'):
        state,reason='PARTIAL','POLICY_CHANGED_RECOMPUTE_REQUIRED'
    elif dispute:
        state,reason='DISPUTED',dispute
    elif unalloc>0:
        state,reason='PARTIAL','UNALLOCATED_COST_REMAINS'
    elif not conserve:
        state,reason='HOLD','RECONCILIATION_FAILED'
    else:
        state,reason='RECONCILED','ALL_COSTS_RECONCILED'
    chargeback=bool(state=='RECONCILED' and c.get('showback_chargeback_mode')=='CHARGEBACK' and c.get('chargeback_authorized'))
    return {'reconciliation_state':state,'reason_code':reason,'allocated_total':alloc,'unallocated_total':unalloc,'target_allocations':rounded,'allocation_conserves_total':conserve,'chargeback_allowed':chargeback,'evidence_ref':c.get('evidence_ref')}
