"""Session 50: fixed eight-convention key-skip screen with 600-rune controls."""
from pathlib import Path
import hashlib,json,math,random,itertools
import numpy as np
from lp_core import vals,lp
from word_core import GROUPS
from keyskip_audit import SEEDS,encrypt,accuracy,label
ROOT=Path(__file__).resolve().parent
VARIANTS=list(itertools.product(['big','little'],[0,1],[False,True]))
CANDIDATES=[(s,v) for s in SEEDS for v in VARIANTS]

def stream(seed,v):
    endian,start,separator=v;out=[];counter=start
    while len(out)<2048:
        b=hashlib.sha256(seed.encode()+ (b'\0' if separator else b'') +counter.to_bytes(4,endian)).digest();counter+=1
        out.extend(x%29 for x in b if x<232)
    return out[:2048]
KEYS={(s,v):stream(s,v) for s,v in CANDIDATES}

def model(excluded):
    u=np.ones(29)*.5;b=np.zeros((29,29));t=np.zeros((29,29,29))
    for name,source in GROUPS.items():
        if name==excluded:continue
        a=vals(source)
        for x in a:u[x]+=1
        for x,y in zip(a,a[1:]):b[x,y]+=1
        for x,y,z in zip(a,a[1:],a[2:]):t[x,y,z]+=1
    u/=u.sum();b=(b+5*u)/(b.sum(1,keepdims=True)+5);t=(t+5*b[None,:,:])/(t.sum(2,keepdims=True)+5)
    return np.log(u),np.log(b),np.log(t)

def decode(c,k,m,initial=None):
    u,b,t=m
    states=[(0.,0,())] if initial is None else [initial]
    for i in range(len(states[0][2]),len(c)):
        y=c[i];nxt={}
        for score,j,path in states:
            for s in range(3 if i else 1):
                x=(y-k[j+s])%29
                if any((x+k[a])%29!=c[i-1] for a in range(j,j+s)):continue
                ll=float(u[x] if i==0 else b[path[-1],x] if i==1 else t[path[-2],path[-1],x])
                ll+=s*math.log(.82)+(math.log(.18) if i and y==c[i-1] else 0.)
                p=path+(x,);state=(j+s+1,p[-2:]);v=(score+ll,j+s+1,p)
                if state not in nxt or v[0]>nxt[state][0]:nxt[state]=v
        states=sorted(nxt.values(),reverse=True)[:128]
    return states[0]

def screen(c,m):
    ranked=sorted([(decode(c[:200],KEYS[s,v],m),(s,v)) for s,v in CANDIDATES],key=lambda x:x[0][0],reverse=True)
    initial,key=ranked[0];final=decode(c[:600],KEYS[key],m,initial)
    return ranked,initial,final,key

def main():
    controls=[]
    for group in ['koan','divinity']:
        p=list(map(int,vals(GROUPS[group])[:600]));m=model(group)
        for z,v in enumerate(VARIANTS):
            key=(SEEDS[z],v);c,skips=encrypt(p,KEYS[key],random.Random(5000+z+(100 if group=='divinity' else 0)))
            ranked,initial,final,selected=screen(c,m)
            row={'held_out_group':group,'planted':key,'selected':selected,'rank':1+[k for _,k in ranked].index(key),'prefix_accuracy':accuracy(p[:200],initial[2]),'continuation_accuracy':accuracy(p[200:],final[2][200:]),'total_skips':sum(skips),'max_skip':max(skips),'prefix_score':initial[0]/200,'continuation_score':(final[0]-initial[0])/400}
            controls.append(row)
    gate=all(r['rank']==1 and r['prefix_accuracy']>=.9 and r['continuation_accuracy']>=.9 for r in controls)
    out={'conventions':VARIANTS,'seeds':SEEDS,'controls':controls,'gate':gate,'spec':{'prefix':200,'continuation':400,'width':128,'maximum_skip_per_rune':2,'suppression':.82,'gate':'Every control: planted key rank 1, prefix and locked continuation accuracy >=90%; no tuning after run','real_model_excludes':'koan','key':'SHA256(UTF8(seed)||optional zero byte||uint32 counter); endian/start from convention; digest bytes <232 modulo29; positive additive encryption; zero initial offset; section resets'}}
    if gate:
        real=[];m=model('koan')
        for section,source in enumerate('%'.join(lp[:56]).split('$')):
            c=list(map(int,vals(source)))
            if len(c)<600:continue
            ranked,initial,final,key=screen(c,m)
            real.append({'section':section,'selected':key,'prefix_score':initial[0]/200,'continuation_score':(final[0]-initial[0])/400,'decoded_runes':final[2],'continuation_labels':label(final[2][200:]),'rankings':[{'key':k,'score':state[0]/200} for state,k in ranked]})
        out['real']=real
    out['source_sha256']=hashlib.sha256((ROOT/'liber-primus__transcription--master.txt').read_bytes()).hexdigest()
    (ROOT/'key_conventions_results.json').write_text(json.dumps(out,indent=2)+'\n')
    print('gate',gate)
    for r in controls:print(r['held_out_group'],r['planted'],r['rank'],r['prefix_accuracy'],r['continuation_accuracy'],r['max_skip'])
    for r in out.get('real',[]):print('real',r['section'],r['selected'],r['prefix_score'],r['continuation_score'])
if __name__=='__main__':main()
