"""Fiction pédagogique, tarifs Sonnet 5.5 au 24/10/2026. Aucun appel réseau.
Usage: python3 cout_cache_claude.py. Ni moteur de cache ni facture fournisseur.
"""
from copy import deepcopy
from decimal import Decimal as D
import json

RATES = tuple(map(D, ['2', '2.5', '4', '.1', '10']))
PROFILE = ('claude-sonnet-5-5', '2026-10-24', 'standard-global-no-tools')

def record(key, write5=0, write60=0, read=0):
    return dict(request_key=key, model=PROFILE[0], day=PROFILE[1], profile=PROFILE[2], usage=dict(
        input_tokens=1000, output_tokens=500, cache_read_input_tokens=read,
        cache_creation_input_tokens=write5+write60,
        cache_creation=dict(ephemeral_5m_input_tokens=write5, ephemeral_1h_input_tokens=write60)))

ROWS = [record('R1',10000), record('R2',read=10000), record('R3',write60=10000),
        record('R4',read=10000), record('R5',2000,1000,6000)]

def counters(row):
    if (row.get('model'),row.get('day'),row.get('profile')) != PROFILE:
        raise ValueError('Tarif ou périmètre non couvert')
    if not isinstance(row.get('request_key'),str) or not row['request_key']:
        raise ValueError('Identité manquante')
    try:
        u=row['usage']; c=u['cache_creation']
        values=(u['input_tokens'],c['ephemeral_5m_input_tokens'],c['ephemeral_1h_input_tokens'],u['cache_read_input_tokens'],u['output_tokens'])
        aggregate=u['cache_creation_input_tokens']
    except (KeyError,TypeError):
        raise ValueError('Compteurs incomplets') from None
    if any(type(n) is not int or n<0 for n in (*values,aggregate)):
        raise ValueError('Compteur non entier positif ou nul')
    if aggregate != values[1]+values[2]:
        raise ValueError('Création incohérente')
    return values

def cost(row):
    return sum(D(n)*rate for n,rate in zip(counters(row),RATES))/D(1000000)

def ledger(rows):
    seen={};total=D(0)
    for row in rows:
        counters(row)
        key=row['request_key']
        if key in seen:
            if row != seen[key]: raise ValueError('Même requête, mesures différentes')
            continue
        seen[key]=deepcopy(row);total+=cost(row)
    return total

def main():
    checks=0; refused=[]
    def check(condition):
        nonlocal checks
        assert condition;checks+=1
    def reject(name,rows):
        try: ledger(rows)
        except ValueError: refused.append(name);check(True)
        else: raise AssertionError(name)
    expected=['.032','.008','.047','.008','.0166']
    for row,amount in zip(ROWS,expected):check(cost(row)==D(amount))
    total=ledger(ROWS);check(total==D('.1116'))
    check(ledger(list(reversed(ROWS)))==total)
    check(ledger(ROWS+[deepcopy(ROWS[4])])==total)
    tokens=[sum(counters(r)[i] for r in ROWS) for i in range(5)]
    check(tokens==[5000,12000,11000,26000,2500])
    check(sum(tokens[:4])==54000)
    baseline=D(54000)*2/1000000+D(2500)*10/1000000
    check(baseline==D('.133'));check(baseline-total==D('.0214'))
    check(sum(r['usage']['cache_read_input_tokens']>0 for r in ROWS)==3)
    check(D('11.16')/100==total) # montant fictif cost_report en cents USD
    check(cost(ROWS[0])+cost(ROWS[1])==D('.040'))
    check(cost(ROWS[2])+cost(ROWS[3])==D('.055'))
    check(D('.055')>D('.054'))
    check(cost(ROWS[2])+2*cost(ROWS[3])==D('.063'))
    check(D('.063')<D('.081'))
    # Trois erreurs volontaires : elles doivent différer des oracles.
    old_read=total+D(26000)*D('.1')/1000000
    check(old_read==D('.1142') and old_read!=total)
    double_creation=cost(ROWS[4])+D(3000)*D('2.5')/1000000
    check(double_creation==D('.0241') and double_creation!=D('.0166'))
    check(ROWS[1]['usage']['input_tokens']-ROWS[1]['usage']['cache_read_input_tokens']<0)
    for field,val in [('model','other'),('day','2026-10-06'),('profile','batch'),('request_key','')]:
        r=deepcopy(ROWS[0]);r[field]=val;reject(field,[r])
    for value in [-1,1.5,True,'1000',None]:
        r=deepcopy(ROWS[0]);r['usage']['input_tokens']=value;reject('type '+repr(value),[r])
    for field in ['input_tokens','output_tokens','cache_read_input_tokens','cache_creation_input_tokens','cache_creation']:
        r=deepcopy(ROWS[0]);del r['usage'][field];reject('absent '+field,[r])
    r=deepcopy(ROWS[4]);r['usage']['cache_creation_input_tokens']=2999;reject('somme incohérente',[r])
    r=deepcopy(ROWS[0]);r['usage']['cache_creation']=None;reject('détail null',[r])
    r=deepcopy(ROWS[0]);r['usage']['output_tokens']=501;reject('conflit identité',ROWS+[r])
    print(json.dumps(dict(checks=checks,rejectedCases=refused,costs=expected,totalUsd=str(total),baselineUsd=str(baseline),differenceUsd=str(baseline-total),tokens=tokens,requestHits='3/5',inputReadShare='26000/54000',witnesses=dict(oldReadUsd=str(old_read),doubleCreationR5Usd=str(double_creation)),scope='Fiction; usage final complet fourni; aucun cache/API/facture exécuté'),ensure_ascii=False,indent=2))
if __name__=='__main__':main()
