// rl_multires_features_update.cu — per-batch multi-resolution features
// derived from real-time timestamp deltas across 3 horizons.
//
// Maintains streaming EMA state per (batch, horizon, feature) and
// outputs 12 normalized features [B × 3 × 4]:
//   per horizon h ∈ {1s, 10s, 600s}:
//     [0] price_change  = EMA of (mid - prev_mid) / σ_price
//     [1] vol_realized  = sqrt(EMA of (mid - prev_mid)²)
//     [2] oflow_imb     = EMA of (bid_sz[0] - ask_sz[0]) / (bid_sz[0] + ask_sz[0])
//     [3] trade_burst   = EMA of event_rate  (1/dt_seconds, capped at 1000)
//
// The EMA alpha per horizon = dt / horizon_seconds (time-weighted
// exponential: a 1-second event on a 10s horizon contributes α=0.1).
// This gives identical time-constant semantics to a circular buffer
// with O(1) state per feature.
//
// State buffers (persistent across steps):
//   multires_state_d [B × 3 × 4]: running EMA values
//   prev_mid_d [B]: previous step's mid price
//   prev_ts_ns_d [B]: previous step's timestamp (for dt computation)
//
// One thread per batch. No shared memory.
// Per `feedback_no_atomicadd`, `feedback_cpu_is_read_only`.

#include <stdint.h>

#define N_HORIZONS       3
#define N_FEATURES       4
#define MULTIRES_DIM     (N_HORIZONS * N_FEATURES)

#define RL_MULTIRES_HORIZON_1_INDEX  521

extern "C" __global__ void rl_multires_features_update(
    float*         __restrict__ multires_state,   // [B × MULTIRES_DIM] IN/OUT
    float*         __restrict__ multires_output,  // [B × MULTIRES_DIM] OUT (normalized)
    float*         __restrict__ prev_mid,         // [B] IN/OUT
    unsigned long long* __restrict__ prev_ts_ns,  // [B] IN/OUT
    const float*   __restrict__ bid_px,           // [BOOK_LEVELS]
    const float*   __restrict__ ask_px,           // [BOOK_LEVELS]
    const float*   __restrict__ bid_sz,           // [BOOK_LEVELS]
    const float*   __restrict__ ask_sz,           // [BOOK_LEVELS]
    const float*   __restrict__ isv,
    const unsigned long long* __restrict__ current_ts_ns_ptr,
    int                           b_size
) {
    const int b = blockIdx.x * blockDim.x + threadIdx.x;
    if (b >= b_size) return;

    const float mid = 0.5f * (bid_px[0] + ask_px[0]);
    const float old_mid = prev_mid[b];
    const unsigned long long current_ts_ns = current_ts_ns_ptr[0];
    const unsigned long long old_ts = prev_ts_ns[b];

    // dt in seconds (protect against zero/backward timestamps).
    const float dt = (current_ts_ns > old_ts)
        ? (float)(current_ts_ns - old_ts) * 1e-9f
        : 1e-6f;

    const float price_delta = mid - old_mid;
    const float best_bid_sz = bid_sz[0];
    const float best_ask_sz = ask_sz[0];
    const float total_sz = best_bid_sz + best_ask_sz + 1e-8f;
    const float imbalance = (best_bid_sz - best_ask_sz) / total_sz;
    const float event_rate = fminf(1.0f / (dt + 1e-8f), 1000.0f);

    const int base = b * MULTIRES_DIM;

    for (int h = 0; h < N_HORIZONS; ++h) {
        const float horizon_s = isv[RL_MULTIRES_HORIZON_1_INDEX + h];
        const float alpha = fminf(dt / (horizon_s + 1e-8f), 1.0f);
        const float one_minus_alpha = 1.0f - alpha;

        const int off = base + h * N_FEATURES;

        // EMA updates.
        float ema_price = multires_state[off + 0];
        float ema_vol_sq = multires_state[off + 1];
        float ema_imb = multires_state[off + 2];
        float ema_burst = multires_state[off + 3];

        // First-observation bootstrap (sentinel = 0 for all EMAs).
        if (old_ts == 0ULL) {
            ema_price = price_delta;
            ema_vol_sq = price_delta * price_delta;
            ema_imb = imbalance;
            ema_burst = event_rate;
        } else {
            ema_price = one_minus_alpha * ema_price + alpha * price_delta;
            ema_vol_sq = one_minus_alpha * ema_vol_sq + alpha * (price_delta * price_delta);
            ema_imb = one_minus_alpha * ema_imb + alpha * imbalance;
            ema_burst = one_minus_alpha * ema_burst + alpha * event_rate;
        }

        multires_state[off + 0] = ema_price;
        multires_state[off + 1] = ema_vol_sq;
        multires_state[off + 2] = ema_imb;
        multires_state[off + 3] = ema_burst;

        // Normalized output.
        const float vol = sqrtf(ema_vol_sq + 1e-8f);
        multires_output[off + 0] = ema_price / (vol + 1e-8f);
        multires_output[off + 1] = vol;
        multires_output[off + 2] = ema_imb;
        multires_output[off + 3] = ema_burst / 100.0f;
    }

    prev_mid[b] = mid;
    prev_ts_ns[b] = current_ts_ns;
}
