//! Strategy seed runner.
//!
//! Seeds are curated, named strategies that are upserted into the database on
//! API startup. They are environment-aware: `common` seeds run in every
//! environment; `dev` seeds run only when `seed_env = "dev"` is set in the
//! database config.
//!
//! Seeds are defined as Rust structs holding `serde_json::Value` configs so
//! that hash computation reuses the existing [`normalize_strategy`] /
//! [`compute_strategy_hash`] functions — no hash duplication in SQL.

mod common;
mod dev;

use crate::error::DalError;
use crate::models::strategy_config::StrategyConfig;
use crate::repo::strategy;
use crate::strategy_hash::{compute_strategy_hash, normalize_strategy};
use serde_json::Value;
use sqlx::PgPool;
use tracing::info;

/// A named, versioned strategy definition used for seeding.
pub struct StrategySeed {
    pub name: &'static str,
    pub version: i32,
    pub description: &'static str,
    pub config: Value,
}

/// Upsert all seed strategies appropriate for `env`.
///
/// - `"common"` seeds run for every environment.
/// - `"dev"` seeds additionally run when `env == "dev"`.
///
/// Each seed is upserted by `(name, version)`, so re-running is idempotent.
pub async fn run_seeds(pool: &PgPool, env: &str) -> Result<(), DalError> {
    let seeds = common::strategies();
    seed_batch(pool, "common", &seeds).await?;

    if env == "dev" {
        let dev_seeds = dev::strategies();
        seed_batch(pool, "dev", &dev_seeds).await?;
    }

    Ok(())
}

async fn seed_batch(pool: &PgPool, label: &str, seeds: &[StrategySeed]) -> Result<(), DalError> {
    for seed in seeds {
        // Round-trip through StrategyConfig so the stored config and hash are in
        // the canonical serialization form — identical to what the backend computes
        // when a paper run is submitted. Without this, seeds with redundant defaults
        // (e.g. explicit "offset": 0) would produce a different hash than at run time.
        let canonical: Value = serde_json::from_value::<StrategyConfig>(seed.config.clone())
            .map(|typed| serde_json::to_value(&typed).expect("StrategyConfig always serializes"))
            .unwrap_or_else(|_| seed.config.clone());

        let hash_input = normalize_strategy(&canonical);
        let hash = compute_strategy_hash(&hash_input);
        strategy::upsert_named(
            pool,
            seed.name,
            seed.version,
            seed.description,
            &hash,
            &canonical,
            &hash_input,
        )
        .await?;
        info!(name = seed.name, version = seed.version, env = label, "seeded strategy");
    }
    Ok(())
}
