//! Per-backtest sim parameter arrays. Replaces the scalar-broadcast
//! parameters previously passed to `step_decision_with_latency`.
//! `from_uniform` rebuilds the legacy uniform behaviour for smoke /
//! fixture tests where every backtest shares the same config.
//! `from_grid` packs a sweep cell-grid (n_parallel=140 typical) into
//! one BatchedSimConfig.

use anyhow::{anyhow, Result};

/// Single source of truth for per-backtest sim params at run time.
/// Lengths MUST all equal `n_backtests`.
#[derive(Clone, Debug)]
pub struct BatchedSimConfig {
    pub target_annual_vol_units: Vec<f32>,
    pub annualisation_factor:    Vec<f32>,
    pub max_lots:                Vec<u16>,
    pub latency_ns:              Vec<u32>,
    pub kelly_frac_floor:        Vec<f32>,
    pub sharpe_weight_floor:     Vec<f32>,
    // P4: threshold gate + per-fill cost. threshold = absolute conviction
    // cutoff (max|p_h - 0.5| * 2 below threshold → noop). cost is deducted
    // per fill in apply_fill_to_pos.
    pub threshold:               Vec<f32>,
    pub cost_per_lot_per_side:   Vec<f32>,
}

/// P6: one fully-resolved sim variant for the sweep runner's grid-pack
/// flow. Built from sweep-YAML `SimVariant` entries (per-cell threshold +
/// cost + per-variant overrides) layered over the SweepBase scalars.
#[derive(Clone, Debug)]
pub struct ResolvedSimVariant {
    pub name: String,
    pub target_annual_vol_units: f32,
    pub annualisation_factor:    f32,
    pub max_lots:                u16,
    pub latency_ns:              u32,
    pub kelly_frac_floor:        f32,
    pub sharpe_weight_floor:     f32,
    pub threshold:               f32,
    pub cost_per_lot_per_side:   f32,
}

/// Convenience scalar input for `from_uniform`. Mirror of the historical
/// BacktestHarnessConfig sim fields.
#[derive(Clone, Debug)]
pub struct UniformSimParams {
    pub target_annual_vol_units: f32,
    pub annualisation_factor:    f32,
    pub max_lots:                u16,
    pub latency_ns:              u32,
    pub kelly_frac_floor:        f32,
    pub sharpe_weight_floor:     f32,
    pub threshold:               f32,
    pub cost_per_lot_per_side:   f32,
}

impl BatchedSimConfig {
    pub fn n_backtests(&self) -> usize {
        self.target_annual_vol_units.len()
    }

    pub fn from_uniform(n: usize, p: &UniformSimParams) -> Self {
        Self {
            target_annual_vol_units: vec![p.target_annual_vol_units; n],
            annualisation_factor:    vec![p.annualisation_factor; n],
            max_lots:                vec![p.max_lots; n],
            latency_ns:              vec![p.latency_ns; n],
            kelly_frac_floor:        vec![p.kelly_frac_floor; n],
            sharpe_weight_floor:     vec![p.sharpe_weight_floor; n],
            threshold:               vec![p.threshold; n],
            cost_per_lot_per_side:   vec![p.cost_per_lot_per_side; n],
        }
    }

    /// P6: pack a list of resolved sim variants into one BatchedSimConfig.
    /// Used by the sweep runner when a cell carries N variants (140 typical)
    /// to run n_parallel=N backtests in one pod with shared forward.
    pub fn from_grid(variants: &[ResolvedSimVariant]) -> Self {
        Self {
            target_annual_vol_units: variants.iter().map(|v| v.target_annual_vol_units).collect(),
            annualisation_factor:    variants.iter().map(|v| v.annualisation_factor).collect(),
            max_lots:                variants.iter().map(|v| v.max_lots).collect(),
            latency_ns:              variants.iter().map(|v| v.latency_ns).collect(),
            kelly_frac_floor:        variants.iter().map(|v| v.kelly_frac_floor).collect(),
            sharpe_weight_floor:     variants.iter().map(|v| v.sharpe_weight_floor).collect(),
            threshold:               variants.iter().map(|v| v.threshold).collect(),
            cost_per_lot_per_side:   variants.iter().map(|v| v.cost_per_lot_per_side).collect(),
        }
    }

    pub fn validate(&self) -> Result<()> {
        let n = self.target_annual_vol_units.len();
        let lens = [
            ("annualisation_factor",   self.annualisation_factor.len()),
            ("max_lots",               self.max_lots.len()),
            ("latency_ns",             self.latency_ns.len()),
            ("kelly_frac_floor",       self.kelly_frac_floor.len()),
            ("sharpe_weight_floor",    self.sharpe_weight_floor.len()),
            ("threshold",              self.threshold.len()),
            ("cost_per_lot_per_side",  self.cost_per_lot_per_side.len()),
        ];
        for (name, l) in lens {
            if l != n {
                return Err(anyhow!(
                    "BatchedSimConfig length mismatch: {name} has {l} entries, expected {n}"
                ));
            }
        }
        Ok(())
    }
}
