"""Exact synthetic trainer-score example. Python 3 standard library.
Run: python generate.py. Reads counts.csv beside this script, writes results.json
and weight-sensitivity.csv beside it. No random sampling or empirical data.
Accepted means a binary reference-grade pass, not an optimal-action guarantee.
"""
from pathlib import Path
from fractions import Fraction as F
import csv,json

ROOT=Path(__file__).resolve().parent
def benchmark(rates,weights):
    if set(rates)!=set(weights):
        raise ValueError('Rates and weights must have the same categories')
    if any(w<0 for w in weights.values()) or sum(weights.values())!=1:
        raise ValueError('Nonnegative weights must sum to one')
    if any(rates[k] is None for k,w in weights.items() if w>0):
        raise ValueError('Missing rate in a positive-weight category')
    if any(r is not None and not 0<=r<=1 for r in rates.values()):
        raise ValueError('Rates must be between zero and one')
    return sum((weights[k]*rates[k] for k in weights if weights[k]>0),F(0))

def main():
    rows=list(csv.DictReader((ROOT/'counts.csv').open(newline='',encoding='utf-8')))
    cells={}
    for row in rows:
        key=(row['period'],row['category'])
        a,n=int(row['accepted']),int(row['attempts'])
        if key in cells or not 0<=a<=n or n<0:
            raise ValueError('Duplicate or invalid count cell')
        cells[key]=(a,n)
    expected={(p,c) for p in ('earlier','later') for c in ('familiar','developing')}
    if set(cells)!=expected:
        raise ValueError('Expected two periods and two categories')
    rates={p:{c:F(cells[p,c][0],cells[p,c][1]) if cells[p,c][1] else None for c in ('familiar','developing')} for p in ('earlier','later')}
    pooled={p:F(sum(cells[p,c][0] for c in rates[p]),sum(cells[p,c][1] for c in rates[p])) for p in rates}
    summary={'scope':'synthetic binary accepted decisions; descriptive arithmetic only',
             'rows':rows,'pooled':{p:str(v) for p,v in pooled.items()},'benchmarks':{}}
    for name,w in [('equal',F(1,2)),('earlier_mix',F(4,5)),('later_mix',F(1,5))]:
        weights={'familiar':w,'developing':1-w}
        vals={p:benchmark(rates[p],weights) for p in rates}
        summary['benchmarks'][name]={'familiar_weight':str(w),**{p:str(v) for p,v in vals.items()},'change':str(vals['later']-vals['earlier'])}
    with (ROOT/'weight-sensitivity.csv').open('w',newline='',encoding='utf-8') as f:
        writer=csv.writer(f,lineterminator='\n')
        writer.writerow(['familiar_weight','earlier','later','change'])
        for i in range(101):
            w=F(i,100)
            before=benchmark(rates['earlier'],{'familiar':w,'developing':1-w})
            after=benchmark(rates['later'],{'familiar':w,'developing':1-w})
            writer.writerow(map(str,(w,before,after,after-before)))
    (ROOT/'results.json').write_text(json.dumps(summary,indent=2)+'\n',encoding='utf-8',newline='\n')
    print('Generated exact summary and 101 common-weight rows')
if __name__=='__main__': main()
