"""#777: eth_call sell sim at exit block for hooked v4 test pools. usage: v4sim.py test N"""
import sys, json, csv, random, time
sys.path.insert(0,"/opt/sw"); sys.path.insert(0,"/opt/sw/sellsim")
import sellprobe, sellsim
from sellsim import *
BLK=["latest"]
def call_at(to,data,ov=None,frm=W,value=None):
    return sellprobe.rpc("eth_call",[{"from":frm,"to":to,"data":data},BLK[0]]+([ov] if ov else []))
sellprobe.call=call_at; sellsim.call=call_at
def ts(n): return int(rpc("eth_getBlockByNumber",[hex(n),False])["result"]["timestamp"],16)
bn=int(rpc("eth_blockNumber",[])["result"],16); T1=ts(bn)
def blk_at(t):
    lo,hi=bn-3000000,bn
    while hi-lo>1:
        m=(lo+hi)//2
        if ts(m)<=t: lo=m
        else: hi=m
    return lo
pools={}
for l in open("/opt/sw/v4tape/pools.jsonl"):
    r=json.loads(l); pools[r["pool_id"]]=r
test,N=sys.argv[1],int(sys.argv[2])
rows=[r for r in csv.DictReader(open(f"/root/tm/{test}/trades.csv")) if r.get("arm")=="A"]
random.seed(777); random.shuffle(rows)
pos=[r for r in rows if float(r["net_x"])>0.5][:N]; zer=[r for r in rows if float(r["net_x"])<=0.5][:max(2,N//3)]
rows=pos+zer
res=[]
for r in rows:
    pid=r["pool"]; pl=pools.get(pid)
    if pl and pl["quote"]!="native": continue
    if not pl: res.append(dict(pool=pid,verdict="NO_POOLMETA")); continue
    tok=pl["token"]; ex=int(r["exit_t"]); b=blk_at(ex); BLK[0]=hex(b)
    o=dict(test=test,pool=pid,token=tok,quote=pl["quote"],hook=pl["hooks"],exit_block=b,exit_t=ex,net_x=r["net_x"],hold_s=ex-int(r.get("entry_t",ex)))
    try:
        key=pool_key(pid,pl["block"])
        tsup=int(rpc("eth_call",[{"to":tok,"data":"0x18160ddd"},BLK[0]])["result"],16)
        bb=probe_balance(tok); a=probe_allow(tok,W,P2)
        if not bb or not a: o.update(verdict="INCONCLUSIVE slot_probe"); res.append(o); continue
        slot2=h(k(pad(UR)+k(pad(tok)+k(pad(W)+pad(1)))))
        for lab,frac in(("p1",0.01),("p01",0.001)):
            amt=int(tsup*frac)
            ov={tok:{"stateDiff":{bb[1]:h(pad(amt)),a[1]:h(pad(BIG))}},P2:{"stateDiff":{slot2:h(pad(((1<<48)-1)<<160|(1<<160)-1))}},W:{"balance":"0x"+"f"*20}}
            r1=call_at(UR,build(key,tok,amt,0),ov)
            o[lab+"_ok"]="result" in r1
            if "error" in r1: o[lab+"_rev"]=str(r1["error"].get("data",r1["error"].get("message")))[:100]
            r2=call_at(UR,build(key,tok,amt,1<<120),ov); d=r2.get("error",{}).get("data","")
            if isinstance(d,str) and len(d)>=138: o[lab+"_out_eth"]=int(d[-64:],16)/1e18
        if o.get("p01_out_eth"):
            rate=o["p01_out_eth"]/(int(tsup*0.001))
            for lab,eth in(("e01",0.1),("e25",0.25)):
                amt=int(eth/rate); amt=min(amt,tsup)
                ov={tok:{"stateDiff":{bb[1]:h(pad(amt)),a[1]:h(pad(BIG))}},P2:{"stateDiff":{slot2:h(pad(((1<<48)-1)<<160|(1<<160)-1))}},W:{"balance":"0x"+"f"*20}}
                r1=call_at(UR,build(key,tok,amt,0),ov); o[lab+"_ok"]="result" in r1
                if "error" in r1: o[lab+"_rev"]=str(r1["error"].get("data",r1["error"].get("message")))[:300]
                r2=call_at(UR,build(key,tok,amt,1<<120),ov); d=r2.get("error",{}).get("data","")
                if isinstance(d,str) and len(d)>=138: o[lab+"_out_eth"]=int(d[-64:],16)/1e18; o[lab+"_ratio"]=round(o[lab+"_out_eth"]/eth,3)
        o["verdict"]="PASS" if o.get("p1_ok") and o.get("p01_ok") else "BLOCK/CHECK"
    except Exception as e: o["verdict"]="EXC "+str(e)[:80]
    res.append(o); print(json.dumps(o),flush=True); time.sleep(1)
json.dump(res,open(f"v4sim-{test}.json","w"))
