//! Knowledge graph for storing and retrieving best practices

use std::collections::HashMap;

use serde::{Deserialize, Serialize};

/// Knowledge graph node for best practices
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KnowledgeNode {
    pub id: String,
    pub title: String,
    pub description: String,
    pub category: String,
    pub relevance: u8,
    pub tags: Vec<String>,
}

/// Knowledge graph relationship
#[derive(Debug, Clone)]
pub struct Relationship {
    from: String,
    to: String,
    #[allow(dead_code)]
    relation_type: String,
}

/// Knowledge graph for best practices and system knowledge
pub struct KnowledgeGraph {
    nodes: HashMap<String, KnowledgeNode>,
    relationships: Vec<Relationship>,
}

impl KnowledgeGraph {
    /// Create a new knowledge graph
    pub fn new() -> Self {
        let mut graph = Self {
            nodes: HashMap::new(),
            relationships: Vec::new(),
        };
        graph.populate_best_practices();
        graph
    }

    /// Populate knowledge graph with best practices
    fn populate_best_practices(&mut self) {
        let practices = vec![
            KnowledgeNode {
                id: "bp_001".to_string(),
                title: "Use Configuration as Code".to_string(),
                description: "Always store infrastructure configuration in version control"
                    .to_string(),
                category: "deployment".to_string(),
                relevance: 95,
                tags: vec!["config".to_string(), "iac".to_string()],
            },
            KnowledgeNode {
                id: "bp_002".to_string(),
                title: "Implement Health Checks".to_string(),
                description: "Define health check endpoints for all services".to_string(),
                category: "reliability".to_string(),
                relevance: 90,
                tags: vec!["monitoring".to_string(), "health".to_string()],
            },
            KnowledgeNode {
                id: "bp_003".to_string(),
                title: "Monitor Resource Usage".to_string(),
                description: "Track CPU, memory, and network metrics continuously".to_string(),
                category: "operations".to_string(),
                relevance: 85,
                tags: vec!["monitoring".to_string(), "metrics".to_string()],
            },
            KnowledgeNode {
                id: "bp_004".to_string(),
                title: "Encrypt Data in Transit".to_string(),
                description: "Use TLS for all network communications".to_string(),
                category: "security".to_string(),
                relevance: 100,
                tags: vec!["security".to_string(), "encryption".to_string()],
            },
            KnowledgeNode {
                id: "bp_005".to_string(),
                title: "Implement Access Control".to_string(),
                description: "Use RBAC and principle of least privilege".to_string(),
                category: "security".to_string(),
                relevance: 95,
                tags: vec!["security".to_string(), "access-control".to_string()],
            },
            KnowledgeNode {
                id: "bp_006".to_string(),
                title: "Use Container Images".to_string(),
                description: "Containerize services for consistency and portability".to_string(),
                category: "deployment".to_string(),
                relevance: 88,
                tags: vec!["containers".to_string(), "docker".to_string()],
            },
            KnowledgeNode {
                id: "bp_007".to_string(),
                title: "Implement Automated Testing".to_string(),
                description: "Run unit, integration, and e2e tests in CI/CD".to_string(),
                category: "quality".to_string(),
                relevance: 90,
                tags: vec!["testing".to_string(), "ci-cd".to_string()],
            },
            KnowledgeNode {
                id: "bp_008".to_string(),
                title: "Use Service Mesh".to_string(),
                description: "Implement service-to-service communication control".to_string(),
                category: "architecture".to_string(),
                relevance: 80,
                tags: vec!["architecture".to_string(), "networking".to_string()],
            },
        ];

        for practice in practices {
            self.nodes.insert(practice.id.clone(), practice);
        }
    }

    /// Search best practices by category
    pub fn search_by_category(&self, category: &str) -> Vec<KnowledgeNode> {
        self.nodes
            .values()
            .filter(|node| node.category == category)
            .cloned()
            .collect()
    }

    /// Search best practices by tag
    pub fn search_by_tag(&self, tag: &str) -> Vec<KnowledgeNode> {
        self.nodes
            .values()
            .filter(|node| node.tags.contains(&tag.to_string()))
            .cloned()
            .collect()
    }

    /// Search best practices by relevance threshold
    pub fn search_by_relevance(&self, min_relevance: u8) -> Vec<KnowledgeNode> {
        let mut results: Vec<_> = self
            .nodes
            .values()
            .filter(|node| node.relevance >= min_relevance)
            .cloned()
            .collect();

        results.sort_by(|a, b| b.relevance.cmp(&a.relevance));
        results
    }

    /// Get all categories
    pub fn get_categories(&self) -> Vec<String> {
        let mut categories: Vec<String> = self
            .nodes
            .values()
            .map(|node| node.category.clone())
            .collect();

        categories.sort();
        categories.dedup();
        categories
    }

    /// Get all tags
    pub fn get_tags(&self) -> Vec<String> {
        let mut tags: Vec<String> = self
            .nodes
            .values()
            .flat_map(|node| node.tags.clone())
            .collect();

        tags.sort();
        tags.dedup();
        tags
    }

    /// Add relationship between knowledge nodes
    pub fn add_relationship(&mut self, from: String, to: String, relation_type: String) {
        self.relationships.push(Relationship {
            from,
            to,
            relation_type,
        });
    }

    /// Get related practices
    pub fn get_related(&self, id: &str) -> Vec<KnowledgeNode> {
        let related_ids: Vec<String> = self
            .relationships
            .iter()
            .filter(|rel| rel.from == id)
            .map(|rel| rel.to.clone())
            .collect();

        self.nodes
            .values()
            .filter(|node| related_ids.contains(&node.id))
            .cloned()
            .collect()
    }
}

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