// rl_regime_observer.cu — unified state-machine for risk-stack controllers.
//
// Spec: docs/superpowers/specs/2026-05-31-regime-observer-design.md v3
// Pearls:
//   - [[pearl_kelly_trade_stream_death]] — empirical motivation
//   - [[pearl_dead_signal_resurrection_discipline]] — meta-principle
//
// Per feedback_no_atomicadd: single-thread single-block, no atomics.
// Per feedback_cpu_is_read_only: pure device kernel.
// Per feedback_nvidia_grade_perf_for_kernels: no host branches in capture.
//
// Launch: Grid=(1,1,1), Block=(1,1,1), smem=0.

// ─── ISV slot indices (mirror Rust constants from isv_slots.rs) ─────
#define RL_KELLY_FRACTION_INDEX                  676
#define RL_COOLDOWN_REMAINING_STEPS_INDEX        668
#define RL_SESSION_PNL_WORST_INDEX               684

#define RL_REGIME_DEAD_ZONE_FLAG_INDEX           696
#define RL_REGIME_DEAD_ZONE_DURATION_INDEX       697
#define RL_REGIME_DEAD_ZONE_TIMEOUT_FLAG_INDEX   698
#define RL_REGIME_RECOVERY_FACTOR_INDEX          699
#define RL_REGIME_SESSION_PNL_VARIANCE_EMA_INDEX 700
#define RL_REGIME_TAIL_EVENT_RECENCY_INDEX       701
#define RL_REGIME_SESSION_PNL_VAR_M2_INDEX       702
#define RL_REGIME_SESSION_PNL_VAR_MEAN_INDEX     703
#define RL_REGIME_SESSION_PNL_VAR_COUNT_INDEX    704
#define RL_REGIME_PREV_WORST_PNL_INDEX           705
#define RL_KELLY_EPS_RECOVERY_LIVE_INDEX         706
#define RL_KELLY_EPS_RECOVERY_MIN_INDEX          707
#define RL_KELLY_EPS_RECOVERY_MAX_INDEX          708
#define RL_KELLY_EPS_RECOVERY_N_RECOVERY_INDEX   709
#define RL_REGIME_DEAD_ZONE_MAX_DURATION_INDEX   710
#define RL_REGIME_TAIL_SIGMA_THRESHOLD_INDEX     713

extern "C" __global__ void rl_regime_observer(
    float* __restrict__ isv,
    const int* __restrict__ flat_count_d,
    int b_size
) {
    if (threadIdx.x != 0 || blockIdx.x != 0) return;

    // ─── Phase 1: dead-zone detection ──────────────────────
    // kelly==0.0f: analytic Kelly clamp writes exactly 0.0 when fraction ≤ 0
    //   (see rl_kelly_fraction_controller). Exact equality is the intended test.
    // cooldown==0.0f: integer-valued step counter stored as float; exact 0 is
    //   the only safe comparison point.
    const float kelly        = isv[RL_KELLY_FRACTION_INDEX];           // prior-step value
    const float cooldown     = isv[RL_COOLDOWN_REMAINING_STEPS_INDEX];
    const int   flat_count   = flat_count_d[0];

    const int dead_zone = (kelly == 0.0f)
                       && (flat_count == b_size)
                       && (cooldown == 0.0f) ? 1 : 0;

    isv[RL_REGIME_DEAD_ZONE_FLAG_INDEX] = (float)dead_zone;

    // Duration counter
    float duration = isv[RL_REGIME_DEAD_ZONE_DURATION_INDEX];
    if (dead_zone) duration += 1.0f; else duration = 0.0f;
    isv[RL_REGIME_DEAD_ZONE_DURATION_INDEX] = duration;

    // Safety net: timeout flag fires if dead-zone persists past MAX_DURATION
    const float max_dur = isv[RL_REGIME_DEAD_ZONE_MAX_DURATION_INDEX];
    const int timeout = (duration > max_dur) ? 1 : 0;
    isv[RL_REGIME_DEAD_ZONE_TIMEOUT_FLAG_INDEX] = (float)timeout;

    // ─── Phase 2: session_pnl_change Welford variance ──────
    const float worst_now  = isv[RL_SESSION_PNL_WORST_INDEX];
    const float worst_prev = isv[RL_REGIME_PREV_WORST_PNL_INDEX];
    const float dx         = worst_now - worst_prev;
    isv[RL_REGIME_PREV_WORST_PNL_INDEX] = worst_now;

    float mean   = isv[RL_REGIME_SESSION_PNL_VAR_MEAN_INDEX];
    float m2     = isv[RL_REGIME_SESSION_PNL_VAR_M2_INDEX];
    float count  = isv[RL_REGIME_SESSION_PNL_VAR_COUNT_INDEX];

    // Skip update on bootstrap (both zero)
    if (worst_now != 0.0f || worst_prev != 0.0f) {
        count += 1.0f;
        const float delta  = dx - mean;
        mean += delta / count;
        const float delta2 = dx - mean;
        m2 += delta * delta2;
        isv[RL_REGIME_SESSION_PNL_VAR_MEAN_INDEX]  = mean;
        isv[RL_REGIME_SESSION_PNL_VAR_M2_INDEX]    = m2;
        isv[RL_REGIME_SESSION_PNL_VAR_COUNT_INDEX] = count;
        const float variance = (count > 1.0f) ? (m2 / (count - 1.0f)) : 0.0f;
        isv[RL_REGIME_SESSION_PNL_VARIANCE_EMA_INDEX] = variance;
    }

    // ─── Phase 3: tail-event detection (3σ threshold) ──────
    // `count` is intentionally reused below from Phase 2 (in-register value
    // post-conditional-increment); no re-read from ISV needed.
    const float var       = isv[RL_REGIME_SESSION_PNL_VARIANCE_EMA_INDEX];
    const float sigma_thr = isv[RL_REGIME_TAIL_SIGMA_THRESHOLD_INDEX];
    const float sigma     = sqrtf(var);

    // Require count >= 10 before tail detection. Statistical floor — Welford
    // sample variance is undefined for N<2 and noisy for N<10; exempt from
    // ISV-promotion per "physics/math constants" rule.
    const int sufficient_count = (count >= 10.0f) ? 1 : 0;
    const int is_tail = sufficient_count && (sigma > 0.0f) && (fabsf(dx) > sigma_thr * sigma);

    float recency = isv[RL_REGIME_TAIL_EVENT_RECENCY_INDEX];
    if (is_tail) recency = 0.0f; else recency += 1.0f;
    isv[RL_REGIME_TAIL_EVENT_RECENCY_INDEX] = recency;

    // ─── Phase 4: emit recovery_factor and eps_recovery_live (issue #9, drift-free) ──
    const float n_recovery = isv[RL_KELLY_EPS_RECOVERY_N_RECOVERY_INDEX];
    const float eps_min    = isv[RL_KELLY_EPS_RECOVERY_MIN_INDEX];
    const float eps_max    = isv[RL_KELLY_EPS_RECOVERY_MAX_INDEX];

    const float recovery_factor = fminf(1.0f, recency / fmaxf(n_recovery, 1.0f));
    const float eps_live        = eps_min + (eps_max - eps_min) * recovery_factor;

    isv[RL_REGIME_RECOVERY_FACTOR_INDEX]      = recovery_factor;
    isv[RL_KELLY_EPS_RECOVERY_LIVE_INDEX]     = eps_live;
}
