//! Command execution context

use crate::paths;
use crate::types::ProjectConfig;
use std::path::PathBuf;
use tracing::Level;

/// Shared context for command execution
///
/// This provides access to common state and configuration that most commands need:
/// - Project configuration path
/// - Log level
/// - Working directory
pub struct CommandContext {
    /// Path to the project config file (mecha10.json)
    pub config_path: PathBuf,

    /// Logging level
    pub log_level: Level,

    /// Project working directory
    pub working_dir: PathBuf,

    /// Cached project configuration (loaded lazily)
    /// Note: We don't store this as ProjectConfig doesn't implement Clone
    /// Instead, commands should load it when needed
    _phantom: std::marker::PhantomData<ProjectConfig>,
}

impl CommandContext {
    /// Create a new command context
    pub fn new(config_path: Option<PathBuf>, log_level: Level) -> Self {
        let working_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
        let config_path = config_path.unwrap_or_else(|| working_dir.join(paths::PROJECT_CONFIG));

        Self {
            config_path,
            log_level,
            working_dir,
            _phantom: std::marker::PhantomData,
        }
    }

    /// Load the project configuration
    ///
    /// This loads the configuration from disk each time it's called.
    /// For better performance in commands that access config multiple times,
    /// call this once and store the result.
    pub async fn load_project_config(&self) -> anyhow::Result<ProjectConfig> {
        if !self.config_path.exists() {
            anyhow::bail!(
                "Project configuration not found at {}. Run 'mecha10 init' first.",
                self.config_path.display()
            );
        }

        crate::types::load_project_config(&self.config_path).await
    }

    /// Check if a project is initialized (mecha10.json exists)
    pub fn is_project_initialized(&self) -> bool {
        self.config_path.exists()
    }

    /// Get a path relative to the project root
    pub fn project_path(&self, path: &str) -> PathBuf {
        self.working_dir.join(path)
    }
}

impl Default for CommandContext {
    fn default() -> Self {
        Self::new(None, Level::INFO)
    }
}
