"""Per-coin launch path from GeckoTerminal candles: start cap, time to each level, peaks, stage metrics."""
import json, gzip, sys, math, pandas as pd, numpy as np
sys.path.insert(0, '/tmp/run'); from rpc import rpc
G = json.load(open('/tmp/rl/r20/gecko.json'))
R = json.load(gzip.open('/tmp/run/rt.json.gz', 'rt'))
SUP = {k.lower(): v for k, v in R['supply'].items()}
SX = json.load(open('/tmp/rl/sup_extra.json'))
KL = {k.lower(): v for k, v in R['kline'].items()}
def supply(t):
    if SUP.get(t): return SUP[t]
    if t in SX and SX[t][0]: return SX[t][0] / 10 ** (SX[t][1] or 18)
    r = rpc('eth_call', [{'to': t, 'data': '0x18160ddd'}, 'latest']); d = rpc('eth_call', [{'to': t, 'data': '0x313ce567'}, 'latest'])
    try: return int(r, 16) / 10 ** int(d, 16)
    except Exception: return None
LV = [1e5, 2.5e5, 5e5, 1e6, 2e6, 5e6, 1e7, 2e7]
rows = []
for t, g in G.items():
    if not g.get('pools'): continue
    s = supply(t)
    if not s: continue
    L = g['launch']
    hh = {}
    srcs = [g.get('h_early') or [], g.get('h_main') or [], KL.get(t) or []]
    for cs in srcs:
        for c in cs:
            ts, o, h, l, cl, v = c
            if ts < L - 3600: continue
            if ts not in hh or v > hh[ts][5]: hh[ts] = c
    H = [hh[k] for k in sorted(hh) if hh[k][5] >= 1000]     # only candles with real volume ($1k+/h)
    if not H: continue
    T0 = next((c[0] for c in H if c[5] >= 5000), H[0][0])
    ign_h = (T0 - L) / 3600
    H = [c for c in H if c[0] <= T0 + 8 * 86400]
    L_launch = L; L = T0                                     # clock starts at ignition (first $5k hour)
    cl = [c[4] * s for c in H]; tt = [(c[0] - L) / 3600 for c in H]; vol = [c[5] for c in H]
    in7 = [i for i, x in enumerate(tt) if x <= 168]
    c7 = sorted([cl[i] for i in in7], reverse=True)
    pk7 = (c7[1] if len(c7) > 1 else c7[0]) if c7 else 0
    r = dict(tok=t, sym=g['sym'], src=g['src'], launch=L_launch, ignition=L, ign_after_launch_h=ign_h, supply=s, npools=len(g['pools']), dexes='|'.join(sorted({p['dex'] for p in g['pools']})),
             start_open=H[0][1] * s, h1_close=cl[0], pk7=pk7, ath2=sorted(cl, reverse=True)[1] if len(cl) > 1 else cl[0])
    for Lv in LV:
        i = next((k for k in in7 if cl[k] >= Lv), None); r['h_%g' % Lv] = tt[i] if i is not None else None
    # minute path, first 5 h
    M = sorted(c for c in (g.get('m_early') or []) if c[5] > 0)
    if ign_h > 4: M = []        # minute data only covers the first hours after the first pool
    if M:
        mt = [(c[0] - L_launch) / 60 for c in M]; mc = [c[4] * s for c in M]; mv = [c[5] for c in M]
        r['m_first_open'] = M[0][1] * s; r['m_first_min'] = mt[0]
        for m in (1, 5, 15, 30, 60, 120, 240):
            k = [i for i, x in enumerate(mt) if x <= m]
            r['mc_%dm' % m] = mc[k[-1]] if k else None
            r['vol_%dm' % m] = sum(mv[i] for i in k) if k else 0
            r['max_%dm' % m] = max(mc[i] for i in k) if k else None
    # hourly stage metrics
    for hr in (1, 4, 12, 24, 48, 72):
        k = [i for i, x in enumerate(tt) if x < hr]
        if not k: continue
        r['mc_%dh' % hr] = cl[k[-1]]; r['vol_%dh' % hr] = sum(vol[i] for i in k); r['max_%dh' % hr] = max(cl[i] for i in k)
        r['dd_%dh' % hr] = cl[k[-1]] / max(cl[i] for i in k)
    # path shape to 20M: worst drawdown before 20M, number of >50% dips
    i20 = next((k for k in in7 if cl[k] >= 2e7), None)
    if i20 is not None:
        run = 0; worst = 1; dips = 0; low = False
        for k in range(0, i20 + 1):
            run = max(run, cl[k]); x = cl[k] / run; worst = min(worst, x)
            if x < 0.5 and not low: dips += 1; low = True
            if x > 0.8: low = False
        r['worst_dd_to20'] = worst; r['dips50_to20'] = dips
    rows.append(r)
df = pd.DataFrame(rows)
df.to_pickle('/tmp/rl/r20/paths.pkl')
print(len(df))
