//! Tool management and orchestration

use crate::error::Result;
use crate::phase::ProjectPhase;
use crate::types::ToolMode;
use crate::LifecycleError;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Tool configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolConfig {
    /// Tool name
    pub name: String,
    /// Whether tool is enabled
    pub enabled: bool,
    /// Phases in which tool is active
    pub phases: Vec<ProjectPhase>,
    /// Tool mode
    pub mode: ToolMode,
    /// Tool-specific configuration
    pub config: HashMap<String, serde_json::Value>,
    /// Dependencies on other tools
    pub dependencies: Vec<String>,
}

impl ToolConfig {
    /// Create a new tool config
    pub fn new(name: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            enabled: false,
            phases: vec![],
            mode: ToolMode::Manual,
            config: HashMap::new(),
            dependencies: vec![],
        }
    }

    /// Enable the tool
    pub fn enable(mut self) -> Self {
        self.enabled = true;
        self
    }

    /// Set phases
    pub fn with_phases(mut self, phases: Vec<ProjectPhase>) -> Self {
        self.phases = phases;
        self
    }

    /// Set mode
    pub fn with_mode(mut self, mode: ToolMode) -> Self {
        self.mode = mode;
        self
    }

    /// Add configuration
    pub fn with_config(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
        self.config.insert(key.into(), value);
        self
    }

    /// Add dependency
    pub fn with_dependency(mut self, dep: impl Into<String>) -> Self {
        self.dependencies.push(dep.into());
        self
    }

    /// Check if tool is active in phase
    pub fn is_active_in_phase(&self, phase: ProjectPhase) -> bool {
        self.enabled && self.phases.contains(&phase)
    }
}

/// Tool registry
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ToolRegistry {
    /// Available tools
    pub tools: HashMap<String, ToolDefinition>,
}

/// Tool definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolDefinition {
    /// Tool name
    pub name: String,
    /// Tool description
    pub description: String,
    /// Tool source/path
    pub source: String,
    /// Required features
    pub features: Vec<String>,
}

impl ToolRegistry {
    /// Create new registry with built-in tools
    pub fn builtin() -> Self {
        let mut registry = Self::default();

        // Add tracking tool
        registry.tools.insert(
            "tracking".to_string(),
            ToolDefinition {
                name: "tracking".to_string(),
                description: "Track changes and TODOs".to_string(),
                source: "tools".to_string(),
                features: vec!["sqlite".to_string(), "rest-api".to_string()],
            },
        );

        // Add docs tool
        registry.tools.insert(
            "docs".to_string(),
            ToolDefinition {
                name: "docs".to_string(),
                description: "Documentation lifecycle management".to_string(),
                source: "tools".to_string(),
                features: vec!["mdbook".to_string(), "rag".to_string()],
            },
        );

        // Add provisioning tool
        registry.tools.insert(
            "provisioning".to_string(),
            ToolDefinition {
                name: "provisioning".to_string(),
                description: "Kubernetes provisioning and deployment".to_string(),
                source: "tools".to_string(),
                features: vec!["kcl".to_string(), "terraform".to_string()],
            },
        );

        // Add dev-system tool
        registry.tools.insert(
            "dev_system".to_string(),
            ToolDefinition {
                name: "dev_system".to_string(),
                description: "Development environment setup".to_string(),
                source: "tools".to_string(),
                features: vec!["hooks".to_string(), "profiles".to_string()],
            },
        );

        // Add presentation tool
        registry.tools.insert(
            "presentation".to_string(),
            ToolDefinition {
                name: "presentation".to_string(),
                description: "Presentation and slide generation".to_string(),
                source: "tools".to_string(),
                features: vec!["templates".to_string(), "slidev".to_string()],
            },
        );

        registry
    }
}

/// Tool manager
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolManager {
    tools: HashMap<String, ToolConfig>,
    #[serde(skip)]
    registry: ToolRegistry,
}

impl ToolManager {
    /// Create a new tool manager
    pub fn new(registry: ToolRegistry) -> Self {
        Self {
            tools: HashMap::new(),
            registry,
        }
    }

    /// Create with built-in registry
    pub fn builtin() -> Self {
        Self::new(ToolRegistry::builtin())
    }

    /// Enable a tool
    pub fn enable_tool(&mut self, name: &str) -> Result<()> {
        if !self.registry.tools.contains_key(name) {
            return Err(LifecycleError::tool_not_found(name));
        }

        if let Some(tool) = self.tools.get_mut(name) {
            tool.enabled = true;
        } else {
            let tool = ToolConfig::new(name).enable();
            self.tools.insert(name.to_string(), tool);
        }

        Ok(())
    }

    /// Disable a tool
    pub fn disable_tool(&mut self, name: &str) -> Result<()> {
        if let Some(tool) = self.tools.get_mut(name) {
            tool.enabled = false;
            Ok(())
        } else {
            Err(LifecycleError::tool_not_found(name))
        }
    }

    /// Get active tools for a phase
    pub fn get_active_tools(&self, phase: ProjectPhase) -> Vec<&ToolConfig> {
        self.tools
            .values()
            .filter(|t| t.is_active_in_phase(phase))
            .collect()
    }

    /// Get tool config
    pub fn get_tool(&self, name: &str) -> Option<&ToolConfig> {
        self.tools.get(name)
    }

    /// Get mutable tool config
    pub fn get_tool_mut(&mut self, name: &str) -> Option<&mut ToolConfig> {
        self.tools.get_mut(name)
    }

    /// List all tools
    pub fn list_tools(&self) -> Vec<&ToolConfig> {
        self.tools.values().collect()
    }

    /// Validate tool dependencies
    pub fn validate_dependencies(&self, name: &str) -> Result<()> {
        if let Some(tool) = self.tools.get(name) {
            for dep in &tool.dependencies {
                if !self.tools.contains_key(dep) {
                    return Err(LifecycleError::ToolDependencyError {
                        reason: format!("Dependency '{}' not found for tool '{}'", dep, name),
                    });
                }

                if let Some(dep_tool) = self.tools.get(dep) {
                    if !dep_tool.enabled {
                        return Err(LifecycleError::ToolDependencyError {
                            reason: format!("Dependency '{}' not enabled for tool '{}'", dep, name),
                        });
                    }
                }
            }
            Ok(())
        } else {
            Err(LifecycleError::tool_not_found(name))
        }
    }
}

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

    #[test]
    fn test_tool_config_creation() {
        let tool = ToolConfig::new("tracking").enable();
        assert_eq!(tool.name, "tracking");
        assert!(tool.enabled);
    }

    #[test]
    fn test_tool_registry_builtin() {
        let registry = ToolRegistry::builtin();
        assert!(registry.tools.contains_key("tracking"));
        assert!(registry.tools.contains_key("docs"));
    }

    #[test]
    fn test_tool_manager_enable() {
        let mut manager = ToolManager::builtin();
        assert!(manager.enable_tool("tracking").is_ok());

        let tool = manager.get_tool("tracking").unwrap();
        assert!(tool.enabled);
    }
}
