"""
lib_v2.py -- [2026-10-01] Playbook matching library v2: the user asked to add information that
may carry direction. Per stock-day, per minute (forward-filled), 6 channels:
  P    price vs open / the stock's ATR%                         (as before)
  W    price vs VWAP / ATR%                                     (as before)
  X    0.5 * log(1 + volume vs its 14-day normal)               (as before)
  SPY  SPY price vs its open, %   (market)                      NEW
  IWM  IWM price vs its open, % / 1.3                           NEW
  GAP  (open / previous close - 1) * 100 / ATR%, same every minute   NEW
(buy/sell flow will be channel 7 once the history flow download is done)
Library cache: data/playbook/library_v2.npz (F, C, H, L, keys)
"""
import gzip
import json
import math
import sys
from datetime import datetime
from pathlib import Path
from zoneinfo import ZoneInfo

import numpy as np

TRADE = Path("/var/www/screener/trade")
sys.path.insert(0, str(TRADE))
import reference as REF

ET = ZoneInfo("America/New_York")
BARS = TRADE / "reports" / "open_window" / "bars"
CACHE = TRADE / "data" / "playbook" / "library_v2.npz"
N = 390


def _minutes(rows):
    by = {}
    for b in rows:
        t = datetime.fromtimestamp(b[0], ET) if not isinstance(b[0], datetime) else b[0].astimezone(ET)
        m = (t.hour - 9) * 60 + t.minute - 30
        if 0 <= m < N:
            by[m] = b
    return by


def bench_paths(day):
    p = BARS / f"bench_{day}.json.gz"
    out = {}
    if p.exists():
        d = json.load(gzip.open(p, "rt"))
        for s in ("SPY", "IWM"):
            by = _minutes(d.get(s, []))
            arr = np.zeros(N, np.float32)
            if by:
                o = by[min(by)][1]; last = o
                for m in range(N):
                    if m in by:
                        last = by[m][4]
                    arr[m] = (last / o - 1) * 100
            out[s] = arr
    return out.get("SPY", np.zeros(N, np.float32)), out.get("IWM", np.zeros(N, np.float32))


def day_arrays(rows, ref, day, bench=None):
    by = _minutes(rows)
    if len(by) < 100:
        return None
    o = by[min(by)][1]
    atrp = ref["atr14"] / ref["prev_close"] * 100 if ref.get("atr14") and ref.get("prev_close") else 5.0
    gap = (o / ref["prev_close"] - 1) * 100 / atrp if ref.get("prev_close") else 0.0
    C, H, L, O = np.zeros(N), np.zeros(N), np.zeros(N), np.zeros(N)
    F = np.zeros((N, 6), np.float32)
    spy, iwm = bench if bench is not None else bench_paths(day)
    last, pv, cv = o, 0.0, 0.0
    for m in range(N):
        b = by.get(m); v = 0.0
        if b:
            O[m], H[m], L[m], last, v = b[1], b[2], b[3], b[4], b[5]
            pv += (b[2] + b[3] + b[4]) / 3 * v; cv += v
        else:
            O[m] = H[m] = L[m] = last
        C[m] = last
        vw = pv / cv if cv else last
        F[m] = ((last / o - 1) * 100 / atrp, (last / vw - 1) * 100 / atrp,
                0.5 * math.log1p(v / max(ref["vol_per_min"][m], 1)), spy[m], iwm[m] / 1.3, gap)
    return {"o": o, "C": C, "H": H, "L": L, "O": O, "F": F}


class Lib:
    def __init__(self):
        if CACHE.exists():
            z = np.load(CACHE, allow_pickle=True)
            self.F, self.C, self.H, self.L, self.keys = z["F"], z["C"], z["H"], z["L"], list(z["keys"])
        else:
            F, C, H, L, keys = [], [], [], [], []
            for p in sorted(BARS.glob("bars_20*.json.gz")):
                d = json.load(gzip.open(p, "rt")); day = d["date"]
                bench = bench_paths(day)
                for s in d["top30_current"]:
                    ref = REF.load(day, s)
                    if not ref:
                        continue
                    a = day_arrays(d["symbols"][s]["bars"], ref, day, bench)
                    if a is None:
                        continue
                    F.append(a["F"]); C.append(a["C"].astype(np.float32)); H.append(a["H"].astype(np.float32))
                    L.append(a["L"].astype(np.float32)); keys.append(f"{day}|{s}")
                REF._CACHE.clear()
            self.F, self.C, self.H, self.L, self.keys = np.array(F), np.array(C), np.array(H), np.array(L), keys
            np.savez(CACHE, F=self.F, C=self.C, H=self.H, L=self.L, keys=np.array(keys))
        self.dates = np.array([k.split("|")[0] for k in self.keys])
