use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

/// Configuration for all training run paths - NO hardcoded defaults
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrainingPaths {
    /// Base directory for all training outputs (e.g., /runpod-volume)
    pub base_dir: PathBuf,

    /// Model name (mamba2, tft, dqn, ppo)
    pub model_name: String,

    /// Unique run ID (generated or provided)
    pub run_id: String,
}

impl TrainingPaths {
    /// Create new training paths configuration
    pub fn new<P: Into<PathBuf>, S1: Into<String>, S2: Into<String>>(
        base_dir: P,
        model_name: S1,
        run_id: S2,
    ) -> Self {
        Self {
            base_dir: base_dir.into(),
            model_name: model_name.into(),
            run_id: run_id.into(),
        }
    }

    /// Get training run directory: {base_dir}/training_runs/{model_name}/run_{run_id}
    pub fn run_dir(&self) -> PathBuf {
        self.base_dir
            .join("training_runs")
            .join(&self.model_name)
            .join(format!("run_{}", self.run_id))
    }

    /// Get checkpoints directory
    pub fn checkpoints_dir(&self) -> PathBuf {
        self.run_dir().join("checkpoints")
    }

    /// Get logs directory
    pub fn logs_dir(&self) -> PathBuf {
        self.run_dir().join("logs")
    }

    /// Get hyperopt directory
    pub fn hyperopt_dir(&self) -> PathBuf {
        self.run_dir().join("hyperopt")
    }

    /// Get metrics directory
    pub fn metrics_dir(&self) -> PathBuf {
        self.run_dir().join("metrics")
    }

    /// Create all directories
    pub fn create_all(&self) -> Result<()> {
        std::fs::create_dir_all(self.checkpoints_dir())?;
        std::fs::create_dir_all(self.logs_dir())?;
        std::fs::create_dir_all(self.hyperopt_dir())?;
        std::fs::create_dir_all(self.metrics_dir())?;
        Ok(())
    }
}

/// Generate run ID with timestamp: YYYYMMDD_HHMMSS_type
pub fn generate_run_id(run_type: &str) -> String {
    let now = chrono::Utc::now();
    format!("{}_{}", now.format("%Y%m%d_%H%M%S"), run_type)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_training_paths_creation() {
        let paths = TrainingPaths::new("/tmp/test", "mamba2", "20251028_223000_hyperopt");
        assert_eq!(
            paths.run_dir(),
            PathBuf::from("/tmp/test/training_runs/mamba2/run_20251028_223000_hyperopt")
        );
        assert_eq!(
            paths.checkpoints_dir(),
            PathBuf::from(
                "/tmp/test/training_runs/mamba2/run_20251028_223000_hyperopt/checkpoints"
            )
        );
    }

    #[test]
    fn test_run_id_generation() {
        let run_id = generate_run_id("hyperopt");
        assert!(run_id.contains("hyperopt"));
        assert!(run_id.len() > 15); // YYYYMMDD_HHMMSS + _hyperopt
    }
}
