//! Built-in semantic triggers
//!
//! Provides pre-configured triggers for common use cases.

use super::registry::RustTrigger;
use super::types::{
    TriggerPhase, TriggerContext, TriggerResult,
    TriggerPriority, DeferredAction,
};

/// Similarity-based duplicate detection trigger
///
/// Monitors for highly similar content during pre-write phase
/// and can notify about potential duplicates.
pub struct SimilarityTrigger {
    /// Unique identifier
    id: String,
    /// Similarity threshold (0.0 - 1.0)
    threshold: f32,
    /// Whether to abort on high similarity
    abort_on_duplicate: bool,
}

impl SimilarityTrigger {
    /// Create a new similarity trigger
    pub fn new(threshold: f32) -> Self {
        Self {
            id: "builtin:similarity".to_string(),
            threshold: threshold.clamp(0.0, 1.0),
            abort_on_duplicate: false,
        }
    }

    /// Set whether to abort on high similarity
    pub fn with_abort(mut self, abort: bool) -> Self {
        self.abort_on_duplicate = abort;
        self
    }

    /// Set custom ID
    pub fn with_id(mut self, id: impl Into<String>) -> Self {
        self.id = id.into();
        self
    }
}

impl RustTrigger for SimilarityTrigger {
    fn id(&self) -> &str {
        &self.id
    }

    fn name(&self) -> &str {
        "Similarity Detection Trigger"
    }

    fn phase(&self) -> TriggerPhase {
        TriggerPhase::PreWrite
    }

    fn priority(&self) -> TriggerPriority {
        TriggerPriority::HIGH
    }

    fn should_execute(&self, ctx: &TriggerContext) -> bool {
        ctx.has_neighbors()
    }

    fn execute(&self, ctx: &TriggerContext) -> TriggerResult {
        if let Some(neighbors) = &ctx.neighbors {
            // Check if any neighbor exceeds threshold
            let high_similarity: Vec<_> = neighbors
                .iter()
                .filter(|n| n.similarity >= self.threshold)
                .collect();

            if !high_similarity.is_empty() {
                if self.abort_on_duplicate {
                    return TriggerResult::Abort(format!(
                        "High similarity detected ({}): potential duplicate of {}",
                        high_similarity[0].similarity,
                        high_similarity[0].id
                    ));
                } else {
                    // Defer notification
                    return TriggerResult::Defer(DeferredAction::NotifyDuplicate {
                        candidate_ids: high_similarity.iter().map(|n| n.id.clone()).collect(),
                    });
                }
            }
        }

        TriggerResult::Continue
    }
}

/// Importance auto-adjustment trigger
///
/// Automatically adjusts node importance based on context during on-write phase.
pub struct ImportanceAdjustTrigger {
    /// Unique identifier
    id: String,
    /// Boost factor for nodes with similar neighbors
    similarity_boost: f32,
    /// Minimum similarity for boost to apply
    boost_threshold: f32,
}

impl ImportanceAdjustTrigger {
    /// Create a new importance adjustment trigger
    pub fn new() -> Self {
        Self {
            id: "builtin:importance-adjust".to_string(),
            similarity_boost: 0.1,
            boost_threshold: 0.7,
        }
    }

    /// Set the similarity boost factor
    pub fn with_boost(mut self, boost: f32) -> Self {
        self.similarity_boost = boost.clamp(0.0, 1.0);
        self
    }

    /// Set the boost threshold
    pub fn with_threshold(mut self, threshold: f32) -> Self {
        self.boost_threshold = threshold.clamp(0.0, 1.0);
        self
    }
}

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

impl RustTrigger for ImportanceAdjustTrigger {
    fn id(&self) -> &str {
        &self.id
    }

    fn name(&self) -> &str {
        "Importance Auto-Adjust Trigger"
    }

    fn phase(&self) -> TriggerPhase {
        TriggerPhase::OnWrite
    }

    fn priority(&self) -> TriggerPriority {
        TriggerPriority::NORMAL
    }

    fn execute(&self, ctx: &TriggerContext) -> TriggerResult {
        // If there are similar neighbors, boost importance
        if let Some(neighbors) = &ctx.neighbors {
            let similar_count = neighbors
                .iter()
                .filter(|n| n.similarity >= self.boost_threshold)
                .count();

            if similar_count > 0 {
                let mut node = ctx.node.clone();
                let boost = self.similarity_boost * (similar_count as f32).min(3.0) / 3.0;
                node.importance = (node.importance + boost as f64).min(1.0);
                return TriggerResult::Modify(node);
            }
        }

        TriggerResult::Continue
    }
}

/// Knowledge graph update trigger
///
/// Triggers knowledge graph updates after a write operation.
pub struct KnowledgeGraphTrigger {
    /// Unique identifier
    id: String,
    /// Minimum similarity for creating relations
    relation_threshold: f32,
    /// Relation type to create
    relation_type: String,
}

impl KnowledgeGraphTrigger {
    /// Create a new knowledge graph trigger
    pub fn new() -> Self {
        Self {
            id: "builtin:knowledge-graph".to_string(),
            relation_threshold: 0.7,
            relation_type: "semantic_related".to_string(),
        }
    }

    /// Set the relation threshold
    pub fn with_threshold(mut self, threshold: f32) -> Self {
        self.relation_threshold = threshold.clamp(0.0, 1.0);
        self
    }

    /// Set the relation type
    pub fn with_relation_type(mut self, relation_type: impl Into<String>) -> Self {
        self.relation_type = relation_type.into();
        self
    }
}

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

impl RustTrigger for KnowledgeGraphTrigger {
    fn id(&self) -> &str {
        &self.id
    }

    fn name(&self) -> &str {
        "Knowledge Graph Update Trigger"
    }

    fn phase(&self) -> TriggerPhase {
        TriggerPhase::PostWrite
    }

    fn priority(&self) -> TriggerPriority {
        TriggerPriority::LOW
    }

    fn should_execute(&self, ctx: &TriggerContext) -> bool {
        ctx.has_neighbors()
    }

    fn execute(&self, ctx: &TriggerContext) -> TriggerResult {
        if let Some(neighbors) = &ctx.neighbors {
            let related: Vec<_> = neighbors
                .iter()
                .filter(|n| n.similarity >= self.relation_threshold)
                .map(|n| n.id.clone())
                .collect();

            if !related.is_empty() {
                return TriggerResult::Defer(DeferredAction::UpdateKnowledgeGraph {
                    entity_ids: related,
                    relation_type: self.relation_type.clone(),
                });
            }
        }

        TriggerResult::Continue
    }
}

/// Consolidation trigger
///
/// Triggers memory consolidation when too many similar memories exist.
pub struct ConsolidationTrigger {
    /// Unique identifier
    id: String,
    /// Number of similar memories before triggering consolidation
    threshold_count: usize,
    /// Similarity threshold for counting
    similarity_threshold: f32,
}

impl ConsolidationTrigger {
    /// Create a new consolidation trigger
    pub fn new() -> Self {
        Self {
            id: "builtin:consolidation".to_string(),
            threshold_count: 5,
            similarity_threshold: 0.8,
        }
    }

    /// Set the threshold count
    pub fn with_count(mut self, count: usize) -> Self {
        self.threshold_count = count.max(2);
        self
    }

    /// Set the similarity threshold
    pub fn with_similarity(mut self, threshold: f32) -> Self {
        self.similarity_threshold = threshold.clamp(0.0, 1.0);
        self
    }
}

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

impl RustTrigger for ConsolidationTrigger {
    fn id(&self) -> &str {
        &self.id
    }

    fn name(&self) -> &str {
        "Consolidation Trigger"
    }

    fn phase(&self) -> TriggerPhase {
        TriggerPhase::PostWrite
    }

    fn priority(&self) -> TriggerPriority {
        TriggerPriority::LOWEST
    }

    fn should_execute(&self, ctx: &TriggerContext) -> bool {
        ctx.has_neighbors()
    }

    fn execute(&self, ctx: &TriggerContext) -> TriggerResult {
        if let Some(neighbors) = &ctx.neighbors {
            let similar_count = neighbors
                .iter()
                .filter(|n| n.similarity >= self.similarity_threshold)
                .count();

            if similar_count >= self.threshold_count {
                return TriggerResult::Defer(DeferredAction::TriggerConsolidation {
                    threshold: self.similarity_threshold,
                });
            }
        }

        TriggerResult::Continue
    }
}

/// Timestamp validation trigger
///
/// Validates and potentially corrects timestamp issues.
pub struct TimestampValidationTrigger {
    /// Unique identifier
    id: String,
    /// Maximum allowed future timestamp (milliseconds)
    max_future_ms: i64,
}

impl TimestampValidationTrigger {
    /// Create a new timestamp validation trigger
    pub fn new() -> Self {
        Self {
            id: "builtin:timestamp-validation".to_string(),
            max_future_ms: 60 * 60 * 1000, // 1 hour
        }
    }

    /// Set the maximum allowed future timestamp
    pub fn with_max_future(mut self, max_ms: i64) -> Self {
        self.max_future_ms = max_ms.max(0);
        self
    }

    fn current_time_ms() -> i64 {
        use std::time::{SystemTime, UNIX_EPOCH};
        SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_millis() as i64
    }
}

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

impl RustTrigger for TimestampValidationTrigger {
    fn id(&self) -> &str {
        &self.id
    }

    fn name(&self) -> &str {
        "Timestamp Validation Trigger"
    }

    fn phase(&self) -> TriggerPhase {
        TriggerPhase::PreWrite
    }

    fn priority(&self) -> TriggerPriority {
        TriggerPriority::HIGHEST
    }

    fn execute(&self, ctx: &TriggerContext) -> TriggerResult {
        let now = Self::current_time_ms();
        let node_time = ctx.node.created_at;

        // Check if timestamp is too far in the future
        if node_time > now + self.max_future_ms {
            let mut node = ctx.node.clone();
            node.created_at = now;
            node.last_accessed_at = now;
            return TriggerResult::Modify(node);
        }

        TriggerResult::Continue
    }
}

/// Create a default trigger registry with built-in triggers
pub fn create_default_registry() -> super::registry::TriggerRegistry {
    let registry = super::registry::TriggerRegistry::new();

    // Register built-in triggers (all disabled by default, users opt-in)
    // Note: In production, these would be registered but disabled
    // registry.register(Arc::new(SimilarityTrigger::new(0.9))).ok();
    // registry.register(Arc::new(ImportanceAdjustTrigger::new())).ok();
    // registry.register(Arc::new(KnowledgeGraphTrigger::new())).ok();
    // registry.register(Arc::new(ConsolidationTrigger::new())).ok();
    // registry.register(Arc::new(TimestampValidationTrigger::new())).ok();

    registry
}

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

    #[test]
    fn test_similarity_trigger() {
        let trigger = SimilarityTrigger::new(0.9);
        assert_eq!(trigger.phase(), TriggerPhase::PreWrite);
        assert_eq!(trigger.id(), "builtin:similarity");
    }

    #[test]
    fn test_similarity_trigger_execution() {
        let trigger = SimilarityTrigger::new(0.9);
        
        let node = MemoryNode::new("test");
        let mut ctx = TriggerContext::new(TriggerPhase::PreWrite, node)
            .with_neighbors(vec![
                super::super::types::NeighborInfo {
                    id: "n1".to_string(),
                    similarity: 0.95,
                    content: None,
                },
            ]);

        let result = trigger.execute(&ctx);
        assert!(matches!(result, TriggerResult::Defer(DeferredAction::NotifyDuplicate { .. })));
    }

    #[test]
    fn test_similarity_trigger_abort() {
        let trigger = SimilarityTrigger::new(0.9).with_abort(true);
        
        let node = MemoryNode::new("test");
        let mut ctx = TriggerContext::new(TriggerPhase::PreWrite, node)
            .with_neighbors(vec![
                super::super::types::NeighborInfo {
                    id: "n1".to_string(),
                    similarity: 0.95,
                    content: None,
                },
            ]);

        let result = trigger.execute(&ctx);
        assert!(result.should_abort());
    }

    #[test]
    fn test_importance_adjust_trigger() {
        let trigger = ImportanceAdjustTrigger::new().with_boost(0.2);
        
        let mut node = MemoryNode::new("test");
        node.importance = 0.5;
        
        let mut ctx = TriggerContext::new(TriggerPhase::OnWrite, node)
            .with_neighbors(vec![
                super::super::types::NeighborInfo {
                    id: "n1".to_string(),
                    similarity: 0.8,
                    content: None,
                },
            ]);

        let result = trigger.execute(&ctx);
        if let TriggerResult::Modify(modified) = result {
            assert!(modified.importance > 0.5);
        } else {
            panic!("Expected Modify result");
        }
    }

    #[test]
    fn test_knowledge_graph_trigger() {
        let trigger = KnowledgeGraphTrigger::new().with_threshold(0.8);
        
        let node = MemoryNode::new("test");
        let ctx = TriggerContext::new(TriggerPhase::PostWrite, node)
            .with_neighbors(vec![
                super::super::types::NeighborInfo {
                    id: "n1".to_string(),
                    similarity: 0.85,
                    content: None,
                },
            ]);

        let result = trigger.execute(&ctx);
        assert!(matches!(result, TriggerResult::Defer(DeferredAction::UpdateKnowledgeGraph { .. })));
    }

    #[test]
    fn test_consolidation_trigger() {
        let trigger = ConsolidationTrigger::new().with_count(2);
        
        let node = MemoryNode::new("test");
        let ctx = TriggerContext::new(TriggerPhase::PostWrite, node)
            .with_neighbors(vec![
                super::super::types::NeighborInfo {
                    id: "n1".to_string(),
                    similarity: 0.85,
                    content: None,
                },
                super::super::types::NeighborInfo {
                    id: "n2".to_string(),
                    similarity: 0.82,
                    content: None,
                },
            ]);

        let result = trigger.execute(&ctx);
        assert!(matches!(result, TriggerResult::Defer(DeferredAction::TriggerConsolidation { .. })));
    }
}
