use std::collections::HashMap;

use bt_expr::{compile_expr, resolve_dynamic_periods, ScalarValue};
use bt_kernel::Result;

use crate::orchestrator::StrategyPlan;

pub type ParamGrid = HashMap<String, Vec<ScalarValue>>;

pub fn expand_param_grid(grid: &ParamGrid) -> Vec<HashMap<String, ScalarValue>> {
    if grid.is_empty() {
        return vec![HashMap::new()];
    }

    let mut entries = grid
        .iter()
        .map(|(name, values)| (name.clone(), values.clone()))
        .collect::<Vec<_>>();
    entries.sort_by(|lhs, rhs| lhs.0.cmp(&rhs.0));

    let mut out = Vec::new();
    let mut current = HashMap::new();
    expand_recursive(&entries, 0, &mut current, &mut out);
    out
}

pub fn strategy_with_params(
    strategy: &StrategyPlan,
    params: &HashMap<String, ScalarValue>,
) -> Result<StrategyPlan> {
    let mut merged = strategy.parameters.clone();
    for (name, value) in params {
        merged.insert(name.clone(), value.clone());
    }

    if strategy.has_dynamic_periods {
        // Re-compile from raw expressions with resolved dynamic periods
        let raw_signals = strategy
            .raw_signals
            .as_ref()
            .expect("raw_signals missing for dynamic strategy");
        let raw_sizing = strategy
            .raw_position_sizing
            .as_ref()
            .expect("raw_position_sizing missing");

        let mut compiled_signals = HashMap::new();
        for (name, expr) in raw_signals {
            let resolved = resolve_dynamic_periods(expr, &merged)?;
            compiled_signals.insert(name.clone(), compile_expr(resolved)?);
        }
        let compiled_sizing = compile_expr(resolve_dynamic_periods(raw_sizing, &merged)?)?;

        Ok(StrategyPlan {
            name: strategy.name.clone(),
            signals: compiled_signals,
            position_sizing: compiled_sizing,
            required_columns: strategy.required_columns.clone(),
            required_features: strategy.required_features.clone(),
            parameters: merged,
            metadata: strategy.metadata.clone(),
            raw_signals: strategy.raw_signals.clone(),
            raw_position_sizing: strategy.raw_position_sizing.clone(),
            has_dynamic_periods: true,
        })
    } else {
        // Fast path: just merge params, no re-compilation
        let mut updated = strategy.clone();
        updated.parameters = merged;
        Ok(updated)
    }
}

fn expand_recursive(
    entries: &[(String, Vec<ScalarValue>)],
    index: usize,
    current: &mut HashMap<String, ScalarValue>,
    out: &mut Vec<HashMap<String, ScalarValue>>,
) {
    if index == entries.len() {
        out.push(current.clone());
        return;
    }

    let (name, values) = &entries[index];
    for value in values {
        current.insert(name.clone(), value.clone());
        expand_recursive(entries, index + 1, current, out);
    }
}
