// crates/ml/src/cuda_pipeline/pearl_8_trail_kernel.cu
//
// SP5 Task A7: Pearl 8 — per-direction trail-stop distance from current ATR.
//
// Reads the fold-window's last bar ATR (feature column 9, denormalized via the
// canonical fxcache scheme `atr_abs = max(0.01, exp(atr_norm × 16 − 7))`)
// and writes 4 per-direction trail-distance values:
//
//   Short[270]: atr_abs × ATR_TRAIL_FACTOR=2.0  (standard 2×ATR trail)
//   Hold[271]:  EPS_CLAMP_FLOOR=1.0             (Hold doesn't trail-stop)
//   Long[272]:  atr_abs × ATR_TRAIL_FACTOR=2.0  (same as Short for Layer A)
//   Flat[273]:  EPS_CLAMP_FLOOR=1.0             (Flat doesn't trail-stop)
//
// LAYER A SIMPLIFICATION: Short and Long use the SAME current ATR value.
// The spec's `atr_ema_per_dir[4]` framing implies direction-conditional ATR
// tracking (e.g., ATR observed during Long positions vs Short positions),
// but that requires per-trade-event aggregation infrastructure not yet
// in place. Layer B reads from the 4-slot ISV layout, so a future Layer
// C extension can populate truly direction-specific values without
// breaking the Layer B consumer contract.
//
// 4-thread single-block (one per direction). No atomicAdd.
// __threadfence_system() after writes.
//
// Direction encoding: Short=0, Hold=1, Long=2, Flat=3 (per CLAUDE.md).
//
// Invariant 1 anchors:
//   ATR_TRAIL_FACTOR = 2.0            (industry-standard 2× ATR trail)
//   EPS_CLAMP_FLOOR  = 1.0            (Hold/Flat floor — never zero)
//   ATR_FEATURE_COL  = 9              (fxcache column index for ATR_NORM)
//   log_atr = atr_norm * 16.0 - 7.0  (fxcache normalization constants)
//   atr_abs floor = 0.01              (protect against log-space underflow)

#include <cuda_runtime.h>

extern "C" __global__ void pearl_8_trail_update(
    const float* __restrict__ features,    // [num_bars × market_dim] feature buffer
    int   bar_idx,                         // bar row to read (last bar of fold window)
    int   market_dim,                      // feature stride per bar
    float* __restrict__ scratch_out,
    int   trail_dist_idx_base              // scratch_out base index (4 outputs)
) {
    int d = threadIdx.x;
    if (blockIdx.x != 0 || d >= 4) return;

    const float ATR_TRAIL_FACTOR = 2.0f;  // Invariant 1 anchor: 2× ATR trail distance
    const float EPS_CLAMP_FLOOR  = 1.0f;  // Invariant 1 anchor: Hold/Flat floor

    // Denormalize ATR from feature column 9 (canonical fxcache scheme).
    // Reference: experience_kernels.cu ATR decode pattern.
    // Invariant 1 anchors: 16.0, 7.0 (fxcache normalization constants); 0.01 (floor).
    float atr_norm = features[(long long)bar_idx * market_dim + 9];
    float log_atr  = atr_norm * 16.0f - 7.0f;
    float atr_abs  = fmaxf(expf(log_atr), 0.01f);

    // Per-direction trail distance.
    // Short=0 and Long=2: trade directions that need a real trail distance.
    // Hold=1 and Flat=3:  non-trade directions; trail-stop never fires.
    float trail_dist;
    if (d == 0 || d == 2) {
        // Short (d=0) or Long (d=2): standard 2× ATR trail.
        trail_dist = atr_abs * ATR_TRAIL_FACTOR;
    } else {
        // Hold (d=1) or Flat (d=3): floor value; no trail-stop fires for these.
        trail_dist = EPS_CLAMP_FLOOR;
    }

    scratch_out[trail_dist_idx_base + d] = trail_dist;
    __threadfence_system();
}
