#![allow(
    clippy::cast_possible_truncation,  // usize → u8 is safe (result is 0-100)
)]

//! Multi-tool integration support for unified dashboards
//!
//! Provides trait-based system for tools to register themselves and provide
//! summary information to a central dashboard.
//!
//! # Supported Tools
//!
//! The following tools can be integrated into the TUI dashboard:
//! - **tracking-manager**: Change and TODO tracking with `SQLite` backend
//! - **doc-syntaxis**: Documentation lifecycle and classification
//! - **syntaxis**: Phase and tool orchestration
//! - **presentation-generator**: AI-assisted presentation generation with Slidev
//! - **hello-tool**: Example/template tool demonstrating TUI integration
//!
//! # Integration Pattern
//!
//! Each tool implements the `ToolSummaryProvider` trait:
//!
//! ```ignore
//! pub struct PresentationGeneratorTui;
//!
//! impl ToolSummaryProvider for PresentationGeneratorTui {
//!     fn name(&self) -> &str {
//!         "presentation-generator"
//!     }
//!
//!     fn summary(&self) -> Result<ToolSummary, String> {
//!         // Query presentation data from presentation-generator
//!         // Return metrics: total presentations, drafts, published, etc.
//!         Ok(ToolSummary::new(
//!             "presentation-generator".to_string(),
//!             "Presentation Generator".to_string(),
//!             "AI-assisted Slidev presentation generation".to_string(),
//!             ToolMetrics::new(15, 3, 12, "2024-01-15T10:30:00Z".to_string()),
//!         ).with_commands(vec![
//!             "create".to_string(),
//!             "generate".to_string(),
//!             "list".to_string(),
//!             "export".to_string(),
//!         ]))
//!     }
//!
//!     fn execute_command(&self, command: &str, args: &[String]) -> Result<String, String> {
//!         // Execute commands like:
//!         // - "create" <name> <profile> - Create new presentation
//!         // - "generate" <id> - Generate presentation
//!         // - "export" <id> <format> - Export to PDF/HTML/PPTX
//!         // - "list" - List all presentations
//!         match command {
//!             "create" => { /* ... */ },
//!             "list" => { /* ... */ },
//!             _ => Err(format!("Unknown command: {}", command))
//!         }
//!     }
//! }
//! ```

use std::collections::HashMap;

/// Tool metrics for monitoring
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ToolMetrics {
    /// Total items in tool
    pub total_items: usize,
    /// Active/pending items
    pub active_items: usize,
    /// Completed items
    pub completed_items: usize,
    /// Last operation timestamp (ISO 8601)
    pub last_operation: String,
}

impl ToolMetrics {
    /// Create new metrics
    #[must_use]
    pub const fn new(
        total_items: usize,
        active_items: usize,
        completed_items: usize,
        last_operation: String,
    ) -> Self {
        Self {
            total_items,
            active_items,
            completed_items,
            last_operation,
        }
    }

    /// Get completion percentage
    ///
    /// Returns a value between 0-100, safe to cast to u8.
    #[must_use]
    pub fn completion_percent(&self) -> u8 {
        if self.total_items == 0 {
            0
        } else {
            // SAFETY: .min(100) ensures value is 0-100, which fits safely in u8
            ((self.completed_items * 100) / self.total_items).min(100) as u8
        }
    }
}

/// Tool summary for dashboard display
#[derive(Clone, Debug)]
pub struct ToolSummary {
    /// Tool name
    pub name: String,
    /// Tool display label
    pub label: String,
    /// Tool description
    pub description: String,
    /// Current metrics
    pub metrics: ToolMetrics,
    /// Available commands for this tool
    pub commands: Vec<String>,
}

impl ToolSummary {
    /// Create a new tool summary
    #[must_use]
    pub const fn new(
        name: String,
        label: String,
        description: String,
        metrics: ToolMetrics,
    ) -> Self {
        Self {
            name,
            label,
            description,
            metrics,
            commands: Vec::new(),
        }
    }

    /// Add a command
    #[must_use]
    pub fn add_command(mut self, command: String) -> Self {
        self.commands.push(command);
        self
    }

    /// Add multiple commands
    #[must_use]
    pub fn with_commands(mut self, commands: Vec<String>) -> Self {
        self.commands.extend(commands);
        self
    }
}

/// Trait for tools to provide summary information to dashboards
///
/// Each tool implementing this trait can be registered with a `ToolRegistry`
/// and will provide data to the dashboard.
///
/// # Example
///
/// ```ignore
/// pub struct MyTool;
///
/// impl ToolSummaryProvider for MyTool {
///     fn name(&self) -> &str {
///         "my-tool"
///     }
///
///     fn summary(&self) -> Result<ToolSummary> {
///         Ok(ToolSummary::new(
///             "my-tool".to_string(),
///             "My Tool".to_string(),
///             "Tool description".to_string(),
///             ToolMetrics::new(10, 3, 2, "2024-01-15T10:30:00Z".to_string()),
///         ).with_commands(vec![
///             "list".to_string(),
///             "create".to_string(),
///             "delete".to_string(),
///         ]))
///     }
/// }
/// ```
pub trait ToolSummaryProvider: Send + Sync {
    /// Get tool identifier (kebab-case)
    fn name(&self) -> &str;

    /// Get tool summary information
    ///
    /// # Errors
    /// Returns an error if the tool summary cannot be retrieved.
    fn summary(&self) -> Result<ToolSummary, String>;

    /// Execute a tool command (optional)
    /// Default implementation returns not supported error
    ///
    /// # Errors
    /// Returns an error if the command execution fails or is not supported.
    fn execute_command(&self, _command: &str, _args: &[String]) -> Result<String, String> {
        Err("Command execution not supported".to_string())
    }
}

/// Registry for managing multiple tools
///
/// Allows tools to register themselves and provides access to their summaries.
pub struct ToolRegistry {
    /// Registered tool providers
    providers: HashMap<String, std::sync::Arc<dyn ToolSummaryProvider>>,
}

impl ToolRegistry {
    /// Create a new tool registry
    #[must_use]
    pub fn new() -> Self {
        Self {
            providers: HashMap::new(),
        }
    }

    /// Register a tool provider
    ///
    /// # Errors
    ///
    /// Returns an error if the operation fails
    pub fn register(
        &mut self,
        provider: std::sync::Arc<dyn ToolSummaryProvider>,
    ) -> Result<(), String> {
        let name = provider.name().to_string();
        self.providers.insert(name, provider);
        Ok(())
    }

    /// Get list of registered tool names
    #[must_use]
    pub fn tools(&self) -> Vec<String> {
        self.providers.keys().cloned().collect()
    }

    /// Get summary for a specific tool
    ///
    /// # Errors
    ///
    /// Returns an error if the operation fails
    pub fn summary(&self, name: &str) -> Result<ToolSummary, String> {
        self.providers
            .get(name)
            .ok_or_else(|| format!("Tool '{name}' not found"))?
            .summary()
    }

    /// Get summaries for all registered tools
    #[must_use]
    pub fn all_summaries(&self) -> Vec<ToolSummary> {
        self.providers
            .values()
            .filter_map(|p| p.summary().ok())
            .collect()
    }

    /// Execute a command on a tool
    ///
    /// # Errors
    ///
    /// Returns an error if the operation fails
    pub fn execute(&self, tool: &str, command: &str, args: &[String]) -> Result<String, String> {
        self.providers
            .get(tool)
            .ok_or_else(|| format!("Tool '{tool}' not found"))?
            .execute_command(command, args)
    }

    /// Check if tool is registered
    #[must_use]
    pub fn has_tool(&self, name: &str) -> bool {
        self.providers.contains_key(name)
    }

    /// Get number of registered tools
    #[must_use]
    pub fn tool_count(&self) -> usize {
        self.providers.len()
    }
}

impl Default for ToolRegistry {
    fn default() -> Self {
        Self::new()
    }
}

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

    #[test]
    fn test_tool_metrics_new() {
        let metrics = ToolMetrics::new(10, 3, 2, "2024-01-15T10:30:00Z".to_string());
        assert_eq!(metrics.total_items, 10);
        assert_eq!(metrics.active_items, 3);
        assert_eq!(metrics.completed_items, 2);
    }

    #[test]
    fn test_tool_metrics_completion_percent() {
        let metrics = ToolMetrics::new(10, 3, 5, "2024-01-15T10:30:00Z".to_string());
        assert_eq!(metrics.completion_percent(), 50);
    }

    #[test]
    fn test_tool_metrics_completion_percent_zero() {
        let metrics = ToolMetrics::new(0, 0, 0, "2024-01-15T10:30:00Z".to_string());
        assert_eq!(metrics.completion_percent(), 0);
    }

    #[test]
    fn test_tool_metrics_completion_percent_full() {
        let metrics = ToolMetrics::new(10, 0, 10, "2024-01-15T10:30:00Z".to_string());
        assert_eq!(metrics.completion_percent(), 100);
    }

    #[test]
    fn test_tool_metrics_clone() {
        let metrics1 = ToolMetrics::new(10, 3, 2, "2024-01-15T10:30:00Z".to_string());
        let metrics2 = metrics1.clone();
        assert_eq!(metrics1, metrics2);
    }

    #[test]
    fn test_tool_summary_new() {
        let metrics = ToolMetrics::new(10, 3, 2, "2024-01-15T10:30:00Z".to_string());
        let summary = ToolSummary::new(
            "tool".to_string(),
            "Tool".to_string(),
            "Description".to_string(),
            metrics,
        );

        assert_eq!(summary.name, "tool");
        assert_eq!(summary.label, "Tool");
        assert_eq!(summary.commands.len(), 0);
    }

    #[test]
    fn test_tool_summary_add_command() {
        let metrics = ToolMetrics::new(10, 3, 2, "2024-01-15T10:30:00Z".to_string());
        let summary = ToolSummary::new(
            "tool".to_string(),
            "Tool".to_string(),
            "Description".to_string(),
            metrics,
        )
        .add_command("list".to_string());

        assert_eq!(summary.commands.len(), 1);
        assert_eq!(summary.commands[0], "list");
    }

    #[test]
    fn test_tool_summary_with_commands() {
        let metrics = ToolMetrics::new(10, 3, 2, "2024-01-15T10:30:00Z".to_string());
        let summary = ToolSummary::new(
            "tool".to_string(),
            "Tool".to_string(),
            "Description".to_string(),
            metrics,
        )
        .with_commands(vec!["list".to_string(), "create".to_string()]);

        assert_eq!(summary.commands.len(), 2);
    }

    #[test]
    fn test_tool_registry_new() {
        let registry = ToolRegistry::new();
        assert_eq!(registry.tool_count(), 0);
    }

    #[test]
    fn test_tool_registry_has_tool() {
        let registry = ToolRegistry::new();
        assert!(!registry.has_tool("test"));
    }

    #[test]
    fn test_tool_registry_tools_empty() {
        let registry = ToolRegistry::new();
        let tools = registry.tools();
        assert_eq!(tools.len(), 0);
    }

    #[test]
    fn test_tool_registry_default() {
        let registry = ToolRegistry::default();
        assert_eq!(registry.tool_count(), 0);
    }

    #[test]
    fn test_tool_metrics_equality() {
        let metrics1 = ToolMetrics::new(10, 3, 2, "2024-01-15T10:30:00Z".to_string());
        let metrics2 = ToolMetrics::new(10, 3, 2, "2024-01-15T10:30:00Z".to_string());
        assert_eq!(metrics1, metrics2);
    }

    #[test]
    fn test_tool_metrics_inequality() {
        let metrics1 = ToolMetrics::new(10, 3, 2, "2024-01-15T10:30:00Z".to_string());
        let metrics2 = ToolMetrics::new(10, 3, 3, "2024-01-15T10:30:00Z".to_string());
        assert_ne!(metrics1, metrics2);
    }

    #[test]
    fn test_tool_summary_clone() {
        let metrics = ToolMetrics::new(10, 3, 2, "2024-01-15T10:30:00Z".to_string());
        let summary1 = ToolSummary::new(
            "tool".to_string(),
            "Tool".to_string(),
            "Description".to_string(),
            metrics,
        );
        let summary2 = summary1.clone();
        assert_eq!(summary1.name, summary2.name);
    }
}
