"""DC1.2 T4 survivor baseline (DC Analyst 1, 7 Oct 2026). Prereg: dc-prereg.md line '7 Oct 11:00 | A1 | DC1.2'.
Reads /root/dca1/ev_v4.pkl, ev_v3.pkl (built by dc-a1-1-build.py), liqlock.db (LP class), crew.db (crew root). Read only."""
import pickle, gzip, csv, json, bisect, datetime, collections, sqlite3, statistics
OUT = "/root/dca1"; BKK = datetime.timezone(datetime.timedelta(hours=7))
def bk(t): return datetime.datetime.fromtimestamp(t, BKK).strftime("%Y-%m-%d %H:%M") if t else ""
def day(t): return datetime.datetime.fromtimestamp(t, BKK).strftime("%Y-%m-%d")
T_START = int(datetime.datetime(2026, 9, 15, tzinfo=BKK).timestamp()); T_B = int(datetime.datetime(2026, 9, 29, tzinfo=BKK).timestamp())
T_END = int(datetime.datetime(2026, 10, 6, tzinfo=BKK).timestamp())
H48 = 48 * 3600; PONS_HOOK = "0xe5e702641ea86f4ae6cc3cdaed2b886f976be044"; DEAD = {"0x" + "0" * 40, "0x000000000000000000000000000000000000dead"}
P = {}
for f in ("ev_v4.pkl", "ev_v3.pkl"): P.update(pickle.load(open(OUT + "/" + f, "rb")))
best = {}
for p, d in P.items():
    pk = max(x[6] for x in d["ev"]); key = d["token"] or p
    if key not in best or pk > best[key][0]: best[key] = (pk, p)
coins = [P[p] for _, p in best.values()]
LQ = sqlite3.connect("file:/opt/pricepredict-data/liqlock.db?mode=ro", uri=True, timeout=120)
BT = LQ.execute("SELECT k, t, spb FROM btime ORDER BY k").fetchall(); BTT = [r[1] for r in BT]
LQ_END = BT[-1][1]
def blk(t):
    i = max(0, bisect.bisect_right(BTT, t) - 1); k, t0, spb = BT[i]; return int(k * 1000 + (t - t0) / (spb or 0.1013))
DEND = {"v4": min(max(x[0] for d in coins if d["venue"] == "v4" for x in d["ev"][-1:]), LQ_END)}
DEND["v3"] = DEND["pons_grad"] = max(x[0] for d in coins if d["venue"] != "v4" for x in d["ev"][-1:])
CR = sqlite3.connect("file:/opt/pricepredict-data/crew.db?mode=ro", uri=True, timeout=60)
def root(pool, creator):
    r = CR.execute("SELECT root FROM launch WHERE pool=?", (pool,)).fetchone()
    if r and r[0]: return r[0].lower()
    if creator:
        r = CR.execute("SELECT hop_root FROM wallet WHERE addr=?", (creator,)).fetchone()
        if r and r[0]: return r[0].lower()
    return creator or pool
def lpclass(pool, creator, t):
    """v4 LP control at time t: BURN, CREATOR, OTHER, NONE (from liqlock liq + nft, events before t only)."""
    net = collections.defaultdict(float); senders = set()
    for tid, dl, snd, lt in LQ.execute("SELECT tid, dl, sender, t FROM liq WHERE pool=? AND t<=?", (pool, t)):
        if tid: net[tid] += dl or 0
        elif (dl or 0) > 0: senders.add((snd or "").lower())
    open_ = [k for k, v in net.items() if v > 0]
    if not open_: return "OTHER" if senders else "NONE"
    b = blk(t); owners = []
    for tid in open_:
        r = LQ.execute("SELECT too FROM nft WHERE tid=? AND b<=? ORDER BY b DESC, li DESC LIMIT 1", (tid, b)).fetchone()
        owners.append((r[0] or "").lower() if r else "")
    if all(o in DEAD for o in owners): return "BURN"
    if creator and creator in owners: return "CREATOR"
    return "OTHER"
def Rfix(R, cap):
    if not R or R <= 0: R = 0.1 * cap
    return min(R, 0.5 * cap) if cap > 0 else R
def trade(d, i, ticket=100):
    ev = d["ev"]; pull = d["pull"] or 9e18; t_e, cap_e = ev[i][0], ev[i][1]
    if cap_e <= 0: return None
    R = Rfix(ev[i][7], cap_e); units = ticket * 0.99 * R / (R + ticket) / cap_e
    def out(cap, R):
        if cap <= 0: return 0.0
        v = units * cap; R = Rfix(R, cap); return v * R / (R + v) * 0.99 / ticket
    lastS = None; last = ev[i]
    for j in range(i + 1, len(ev)):
        x = ev[j]
        if x[0] >= pull: return (0.0, "pull", 0)
        if x[0] > t_e + H48: break
        if x[6] >= 3 * cap_e: return (out(x[6], x[7]), "3x", 1)
        if x[6] > 0: lastS = x
        last = x
    if pull <= t_e + H48: return (0.0, "pull", 0)
    # 48 h exit at the last SELLABLE price (a bar close can be broken); no sell since entry = min(last close, entry)
    if lastS: return (out(lastS[6], lastS[7]), "48h", 0)
    return (out(min(last[1], cap_e), last[7]), "48h", 0)
VAR = {"T4-2h": (250_000, 7200), "T4-6h": (250_000, 21600), "T4-1h": (250_000, 3600), "T4-2h-100k": (100_000, 7200),
       "T4-LOCK": (250_000, 0), "T4-PONS": (250_000, 0), "T3": (0, 0)}
rows = []
for d in coins:
    ev = d["ev"]; TS = [x[0] for x in ev]; pull = d["pull"] or 0; t0 = d["t0"]
    if not (T_START <= t0 < T_END): continue
    cr = (d.get("creator") or "").lower(); ispons = d["venue"] == "pons_grad" or d.get("hooks") == PONS_HOOK
    i1m = next((k for k, x in enumerate(ev) if x[6] >= 1_000_000), None); t1m = ev[i1m][0] if i1m is not None else 9e18
    dec = {}
    for name, (L, wait) in VAR.items():
        if name == "T3":
            vol = collections.defaultdict(float)
            for k, x in enumerate(ev):
                if pull and x[0] >= pull: break
                hr = int(x[0] // 3600) * 3600; vol[hr] += x[3] + x[4]
                if hr >= t0 + 86400 and vol[hr] >= 50_000 and sum(vol.get(hr - 3600 * m, 0) for m in range(1, 25)) < 50_000:
                    dec[name] = x[0]; break
            continue
        ic = next((k for k, x in enumerate(ev) if x[6] >= L), None)
        if ic is None: continue
        tc = ev[ic][0]
        if pull and pull <= tc + wait: continue
        if name == "T4-PONS" and not ispons: continue
        if name == "T4-LOCK":
            if d["venue"] != "v4": continue
            c = lpclass(d["pool"], cr, tc); d["lp"] = c
            if c not in ("BURN", "OTHER"): continue
        dec[name] = tc + wait
    if not dec: continue
    cl = (d.get("sym") or "").upper().strip(); rt = root(d["pool"], cr) if d["venue"] == "v4" or cr else d["pool"]
    for name, td in dec.items():
        j = bisect.bisect_left(TS, td)
        if j >= len(ev): continue
        te = ev[j][0]
        if te + H48 > DEND[d["venue"]] or not (T_START <= te < T_END): continue
        r = trade(d, j)
        if not r: continue
        rows.append(dict(variant=name, token=d["token"], pool=d["pool"], venue=d["venue"], sym=d.get("sym") or "", creator=cr, root=rt,
                         lp=d.get("lp", ""), decide=bk(td), entry=bk(te), entry_t=int(te), entry_cap=round(ev[j][1]), before_1m=int(te < t1m),
                         half="A" if te < T_B else "B", mult=round(r[0], 4), reason=r[1], win3x=r[2], day=day(te)))
# one trade per cluster per day per variant (symbol, creator, root): earliest kept
keep = []; seen = collections.defaultdict(set)
for r in sorted(rows, key=lambda r: r["entry_t"]):
    keys = [("s", r["sym"]) if r["sym"] else None, ("c", r["creator"]) if r["creator"] else None, ("r", r["root"])]
    keys = [k for k in keys if k]
    s = seen[(r["variant"], r["day"])]
    if any(k in s for k in keys): continue
    s.update(keys); keep.append(r)
with gzip.open(OUT + "/dc-a1-2-trades.csv.gz", "wt") as f:
    w = csv.DictWriter(f, fieldnames=list(keep[0])); w.writeheader(); w.writerows(keep)
DA = (T_B - T_START) / 86400; DB = (min(T_END, max(DEND.values()) - H48) - T_B) / 86400
DBv4 = (DEND["v4"] - H48 - T_B) / 86400
S = []
for name in VAR:
    for venue in ("all", "v4", "v3", "pons_grad"):
        g = [r for r in keep if r["variant"] == name and (venue == "all" or r["venue"] == venue)]
        if not g: continue
        A = [r for r in g if r["half"] == "A"]; B = [r for r in g if r["half"] == "B"]
        nraw = len([r for r in rows if r["variant"] == name and (venue == "all" or r["venue"] == venue)])
        db = DBv4 if venue == "v4" else DB
        S.append(dict(variant=name, venue=venue, raw=nraw, trades=len(g), per_day=round(len(g) / (DA + db), 1), nA=len(A), nB=len(B),
                      winA=sum(r["win3x"] for r in A), winB=sum(r["win3x"] for r in B),
                      usd_dayA=round(sum((r["mult"] - 1) * 100 for r in A) / DA, 1), usd_dayB=round(sum((r["mult"] - 1) * 100 for r in B) / db, 1),
                      xA=round(statistics.mean([r["mult"] for r in A]), 3) if A else None, xB=round(statistics.mean([r["mult"] for r in B]), 3) if B else None,
                      new_usd_dayA=round(sum((r["mult"] - 1) * 100 for r in A if r["before_1m"]) / DA, 1),
                      new_usd_dayB=round(sum((r["mult"] - 1) * 100 for r in B if r["before_1m"]) / db, 1),
                      pull=round(sum(r["reason"] == "pull" for r in g) / len(g), 2)))
json.dump(dict(days_A=DA, days_B=DB, days_B_v4=DBv4, data_end={k: bk(v) for k, v in DEND.items()}, liqlock_end=bk(LQ_END), rows=S), open(OUT + "/dc12_summary.json", "w"), indent=1)
for s in S: print(json.dumps(s))
print("days A", DA, "B", round(DB, 2), "B v4", round(DBv4, 2), "data end", {k: bk(v) for k, v in DEND.items()}, "liqlock end", bk(LQ_END))
