// crates/ml/src/cuda_pipeline/cost_net_sharpe_kernel.cu
//
// SP15 Phase 1.2 — cost-net sharpe.
//
// Per spec §6.2 per-side semantics:
//   cost_t = commission_per_rt × rt_ind[t]
//          + half_spread[t] × |position[t]| × side_ind[t]
//          + ofi_lambda × |position[t]| × |ofi[t]| × side_ind[t]
//
// Commission is charged at close (rt_ind=1). Half-spread × |position| is
// charged on side-events at entry AND exit (sums to one full spread per
// round-trip). OFI impact same per-side. ofi_lambda is read from
// ISV[OFI_IMPACT_LAMBDA_INDEX=407] (constant across the window;
// constructor-seeded to 2e-4, FoldReset-rewritten to 2e-4 since this is an
// Invariant-1 anchor, not a stateful EMA).
//
// Outputs:
//   isv[COST_PER_BAR_AVG_INDEX=408] = sum(cost_t) / n
//   out[0] = mean(pnl - cost_t)
//   out[1] = std(pnl - cost_t)
//   out[2] = sharpe_net = out[0] / max(out[1], 1e-12)
//
// Same kernel reads LobBar fields from synthetic markets (Phase 2A) and
// real fxcache LOB (prod) — dev/prod parity per Q3.
//
// Single-block launch, BLOCK=256. No atomicAdd (per feedback_no_atomicadd):
// reduction uses shared-memory tree-reduce only. `__threadfence_system()`
// flushes mapped-pinned writes before the host reads via `read_volatile`.
//
// SP15 Wave 3b (2026-05-06): `side_ind` / `rt_ind` are `float*` (0.0/1.0)
// rather than `unsigned int*` to match the type emitted by the
// `sp15_position_history_derivation_kernel`. The Wave 3a kernel signature
// declared u32 inputs; Wave 3b's wire-up pipeline produces f32 indicators
// (one Welford-friendly type, no f32→u32 adapter kernel needed). Internal
// math reads them as floats directly — was `(float)side_ind[i]`, now
// `side_ind[i]`. Bit-equivalent for the production caller (derivation
// emits exact 0.0f/1.0f) and the cost-net oracle test (Wave 3b updates
// it to MappedF32Buffer in the same commit).

extern "C" __global__ void cost_net_sharpe_kernel(
    const float* __restrict__ pnl,
    const float* __restrict__ half_spread,
    const float* __restrict__ ofi,
    const float* __restrict__ side_ind,
    const float* __restrict__ rt_ind,
    const float* __restrict__ position,
    int                 n,
    float               commission_per_rt,
    float*              __restrict__ isv,
    float*              __restrict__ out
) {
    if (blockIdx.x != 0) return;

    const int tid   = (int)threadIdx.x;
    const int BLOCK = (int)blockDim.x;
    __shared__ float reduce_a[256];
    __shared__ float reduce_b[256];
    __shared__ float mean_pnl_shared;
    __shared__ float mean_cost_shared;

    // Read OFI lambda once (slot 407 = OFI_IMPACT_LAMBDA_INDEX).
    const float ofi_lambda = isv[407];

    // Pass 1: parallel sums of pnl_t and cost_t.
    float local_pnl  = 0.0f;
    float local_cost = 0.0f;
    for (int i = tid; i < n; i += BLOCK) {
        local_pnl += pnl[i];
        float c_comm   = commission_per_rt * rt_ind[i];
        float pos_abs  = fabsf(position[i]);
        float c_spread = half_spread[i] * pos_abs * side_ind[i];
        float c_ofi    = ofi_lambda * pos_abs * fabsf(ofi[i]) * side_ind[i];
        local_cost += c_comm + c_spread + c_ofi;
    }
    reduce_a[tid] = local_pnl;
    reduce_b[tid] = local_cost;
    __syncthreads();
    for (int s = BLOCK / 2; s > 0; s >>= 1) {
        if (tid < s) {
            reduce_a[tid] += reduce_a[tid + s];
            reduce_b[tid] += reduce_b[tid + s];
        }
        __syncthreads();
    }
    if (tid == 0) {
        mean_pnl_shared  = (n > 0) ? reduce_a[0] / (float)n : 0.0f;
        mean_cost_shared = (n > 0) ? reduce_b[0] / (float)n : 0.0f;
        // ISV slot 408 = COST_PER_BAR_AVG_INDEX (per sp15_isv_slots.rs).
        isv[408] = mean_cost_shared;
        __threadfence_system();
    }
    __syncthreads();

    const float mean_net = mean_pnl_shared - mean_cost_shared;

    // Pass 2: variance of (pnl_t - cost_t).
    float local_sq = 0.0f;
    for (int i = tid; i < n; i += BLOCK) {
        float c_comm   = commission_per_rt * rt_ind[i];
        float pos_abs  = fabsf(position[i]);
        float c_spread = half_spread[i] * pos_abs * side_ind[i];
        float c_ofi    = ofi_lambda * pos_abs * fabsf(ofi[i]) * side_ind[i];
        float net      = pnl[i] - (c_comm + c_spread + c_ofi);
        float d        = net - mean_net;
        local_sq += d * d;
    }
    reduce_a[tid] = local_sq;
    __syncthreads();
    for (int s = BLOCK / 2; s > 0; s >>= 1) {
        if (tid < s) reduce_a[tid] += reduce_a[tid + s];
        __syncthreads();
    }
    if (tid == 0) {
        float var = (n > 0) ? reduce_a[0] / (float)n : 0.0f;
        float std = sqrtf(fmaxf(var, 1e-12f));
        out[0] = mean_net;
        out[1] = std;
        out[2] = (std > 0.0f) ? mean_net / std : 0.0f;
        __threadfence_system();
    }
}
