//! Trigger Registry implementation
//!
//! Manages registration and execution of semantic triggers.

use std::collections::HashMap;
use std::sync::Arc;

use parking_lot::RwLock;

use super::types::{
    TriggerPhase, TriggerContext, TriggerResult, TriggerConfig,
    TriggerPriority, TriggerError, DeferredAction,
};
use crate::types::MemoryNode;

/// Trait for Rust-native triggers
pub trait RustTrigger: Send + Sync {
    /// Get the trigger's unique identifier
    fn id(&self) -> &str;

    /// Get the trigger's name (for display)
    fn name(&self) -> &str;

    /// Get the phase this trigger operates in
    fn phase(&self) -> TriggerPhase;

    /// Get the trigger's priority
    fn priority(&self) -> TriggerPriority {
        TriggerPriority::NORMAL
    }

    /// Check if the trigger should execute for this context
    fn should_execute(&self, _ctx: &TriggerContext) -> bool {
        true
    }

    /// Execute the trigger
    fn execute(&self, ctx: &TriggerContext) -> TriggerResult;

    /// Get trigger configuration
    fn config(&self) -> TriggerConfig {
        TriggerConfig::default()
    }
}

/// Entry for a registered trigger
struct TriggerEntry {
    /// The trigger implementation
    trigger: Arc<dyn RustTrigger>,
    /// Override configuration (if any)
    config_override: Option<TriggerConfig>,
}

impl TriggerEntry {
    fn is_enabled(&self) -> bool {
        self.config_override.as_ref()
            .map(|c| c.enabled)
            .unwrap_or_else(|| self.trigger.config().enabled)
    }

    fn priority(&self) -> i32 {
        self.config_override.as_ref()
            .map(|c| c.priority)
            .unwrap_or_else(|| self.trigger.priority().0)
    }
}

/// Registry for managing semantic triggers
pub struct TriggerRegistry {
    /// Pre-write triggers
    pre_write: RwLock<Vec<TriggerEntry>>,
    /// On-write triggers
    on_write: RwLock<Vec<TriggerEntry>>,
    /// Post-write triggers
    post_write: RwLock<Vec<TriggerEntry>>,
    /// All registered trigger IDs (for deduplication)
    registered_ids: RwLock<HashMap<String, TriggerPhase>>,
    /// TypeScript callback IDs for pre-write
    ts_pre_write_callbacks: RwLock<Vec<String>>,
    /// TypeScript callback IDs for post-write
    ts_post_write_callbacks: RwLock<Vec<String>>,
    /// Deferred actions queue
    deferred_actions: RwLock<Vec<DeferredAction>>,
}

impl TriggerRegistry {
    /// Create a new empty trigger registry
    pub fn new() -> Self {
        Self {
            pre_write: RwLock::new(Vec::new()),
            on_write: RwLock::new(Vec::new()),
            post_write: RwLock::new(Vec::new()),
            registered_ids: RwLock::new(HashMap::new()),
            ts_pre_write_callbacks: RwLock::new(Vec::new()),
            ts_post_write_callbacks: RwLock::new(Vec::new()),
            deferred_actions: RwLock::new(Vec::new()),
        }
    }

    /// Register a Rust trigger
    pub fn register(&self, trigger: Arc<dyn RustTrigger>) -> Result<(), TriggerError> {
        let id = trigger.id().to_string();
        let phase = trigger.phase();

        // Check for duplicate
        {
            let ids = self.registered_ids.read();
            if ids.contains_key(&id) {
                return Err(TriggerError::DuplicateId(id));
            }
        }

        // Add to appropriate phase list
        let entry = TriggerEntry {
            trigger,
            config_override: None,
        };

        match phase {
            TriggerPhase::PreWrite => {
                let mut triggers = self.pre_write.write();
                triggers.push(entry);
                self.sort_by_priority(&mut triggers);
            }
            TriggerPhase::OnWrite => {
                let mut triggers = self.on_write.write();
                triggers.push(entry);
                self.sort_by_priority(&mut triggers);
            }
            TriggerPhase::PostWrite => {
                let mut triggers = self.post_write.write();
                triggers.push(entry);
                self.sort_by_priority(&mut triggers);
            }
        }

        // Register ID
        self.registered_ids.write().insert(id, phase);

        Ok(())
    }

    /// Unregister a trigger by ID
    pub fn unregister(&self, id: &str) -> Result<(), TriggerError> {
        let phase = {
            let mut ids = self.registered_ids.write();
            ids.remove(id).ok_or_else(|| TriggerError::NotFound(id.to_string()))?
        };

        // Remove from appropriate phase list
        match phase {
            TriggerPhase::PreWrite => {
                let mut triggers = self.pre_write.write();
                triggers.retain(|e| e.trigger.id() != id);
            }
            TriggerPhase::OnWrite => {
                let mut triggers = self.on_write.write();
                triggers.retain(|e| e.trigger.id() != id);
            }
            TriggerPhase::PostWrite => {
                let mut triggers = self.post_write.write();
                triggers.retain(|e| e.trigger.id() != id);
            }
        }

        Ok(())
    }

    /// Update trigger configuration
    pub fn configure(&self, id: &str, config: TriggerConfig) -> Result<(), TriggerError> {
        let phase = {
            let ids = self.registered_ids.read();
            ids.get(id).copied().ok_or_else(|| TriggerError::NotFound(id.to_string()))?
        };

        let update_config = |triggers: &mut Vec<TriggerEntry>| {
            for entry in triggers.iter_mut() {
                if entry.trigger.id() == id {
                    entry.config_override = Some(config.clone());
                    return true;
                }
            }
            false
        };

        match phase {
            TriggerPhase::PreWrite => {
                let mut triggers = self.pre_write.write();
                update_config(&mut triggers);
                self.sort_by_priority(&mut triggers);
            }
            TriggerPhase::OnWrite => {
                let mut triggers = self.on_write.write();
                update_config(&mut triggers);
                self.sort_by_priority(&mut triggers);
            }
            TriggerPhase::PostWrite => {
                let mut triggers = self.post_write.write();
                update_config(&mut triggers);
                self.sort_by_priority(&mut triggers);
            }
        }

        Ok(())
    }

    /// Execute all triggers for a given phase
    pub fn execute_phase(&self, phase: TriggerPhase, ctx: &mut TriggerContext) -> TriggerResult {
        let triggers = match phase {
            TriggerPhase::PreWrite => self.pre_write.read(),
            TriggerPhase::OnWrite => self.on_write.read(),
            TriggerPhase::PostWrite => self.post_write.read(),
        };

        for entry in triggers.iter() {
            // Skip disabled triggers
            if !entry.is_enabled() {
                continue;
            }

            // Check if trigger should execute
            if !entry.trigger.should_execute(ctx) {
                continue;
            }

            // Execute trigger
            match entry.trigger.execute(ctx) {
                TriggerResult::Continue => continue,
                TriggerResult::Abort(reason) => return TriggerResult::Abort(reason),
                TriggerResult::Modify(node) => {
                    ctx.node = node;
                    continue;
                }
                TriggerResult::Defer(action) => {
                    self.deferred_actions.write().push(action);
                    continue;
                }
                TriggerResult::SkipRemaining => return TriggerResult::Continue,
            }
        }

        TriggerResult::Continue
    }

    /// Execute the full trigger pipeline for a write operation
    ///
    /// Returns the final node to be written (potentially modified by triggers)
    /// or an error if any trigger aborted the operation.
    pub fn execute_write_pipeline(
        &self,
        mut node: MemoryNode,
        neighbors: Option<Vec<super::types::NeighborInfo>>,
    ) -> Result<(MemoryNode, Vec<DeferredAction>), String> {
        // Clear deferred actions from previous operations
        self.deferred_actions.write().clear();

        // Phase 1: Pre-Write
        let mut ctx = TriggerContext::new(TriggerPhase::PreWrite, node.clone());
        if let Some(n) = neighbors {
            ctx = ctx.with_neighbors(n);
        }

        match self.execute_phase(TriggerPhase::PreWrite, &mut ctx) {
            TriggerResult::Abort(reason) => return Err(reason),
            _ => {
                node = ctx.node;
            }
        }

        // Phase 2: On-Write
        let mut ctx = TriggerContext::new(TriggerPhase::OnWrite, node.clone());
        match self.execute_phase(TriggerPhase::OnWrite, &mut ctx) {
            TriggerResult::Abort(reason) => return Err(reason),
            _ => {
                node = ctx.node;
            }
        }

        // Collect deferred actions (post-write triggers will be executed after commit)
        let deferred = self.deferred_actions.read().clone();

        Ok((node, deferred))
    }

    /// Execute post-write triggers synchronously
    /// 
    /// Note: For high-throughput scenarios, consider using `AsyncTriggerExecutor`
    /// instead to avoid blocking the main write path.
    pub fn execute_post_write(&self, node: &MemoryNode) -> Vec<DeferredAction> {
        let mut ctx = TriggerContext::new(TriggerPhase::PostWrite, node.clone());
        self.execute_phase(TriggerPhase::PostWrite, &mut ctx);
        
        // Return any new deferred actions
        let deferred = self.deferred_actions.read().clone();
        self.deferred_actions.write().clear();
        deferred
    }
    
    /// Execute the write pipeline and return data for async post-write
    /// 
    /// This method executes Pre-Write and On-Write phases synchronously,
    /// but returns the node and deferred actions for async post-write execution.
    /// 
    /// Use with `AsyncTriggerExecutor` for non-blocking post-write handling:
    /// 
    /// ```rust,ignore
    /// let (final_node, deferred) = registry.execute_write_pipeline_for_async(node, neighbors)?;
    /// 
    /// // Commit the node to storage...
    /// storage.commit(final_node.clone())?;
    /// 
    /// // Submit to async executor (non-blocking)
    /// async_executor.submit_sync(final_node, deferred)?;
    /// ```
    pub fn execute_write_pipeline_for_async(
        &self,
        node: MemoryNode,
        neighbors: Option<Vec<super::types::NeighborInfo>>,
    ) -> Result<(MemoryNode, Vec<DeferredAction>), String> {
        // Same as execute_write_pipeline but returns deferred actions for async processing
        self.execute_write_pipeline(node, neighbors)
    }
    
    /// Get all post-write triggers for registration with AsyncTriggerExecutor
    pub fn get_post_write_triggers(&self) -> Vec<std::sync::Arc<dyn RustTrigger>> {
        self.post_write.read()
            .iter()
            .filter(|e| e.is_enabled())
            .map(|e| e.trigger.clone())
            .collect()
    }

    /// Register a TypeScript callback ID for pre-write phase
    pub fn register_ts_pre_write(&self, callback_id: String) {
        self.ts_pre_write_callbacks.write().push(callback_id);
    }

    /// Register a TypeScript callback ID for post-write phase
    pub fn register_ts_post_write(&self, callback_id: String) {
        self.ts_post_write_callbacks.write().push(callback_id);
    }

    /// Get TypeScript callback IDs for pre-write phase
    pub fn get_ts_pre_write_callbacks(&self) -> Vec<String> {
        self.ts_pre_write_callbacks.read().clone()
    }

    /// Get TypeScript callback IDs for post-write phase
    pub fn get_ts_post_write_callbacks(&self) -> Vec<String> {
        self.ts_post_write_callbacks.read().clone()
    }

    /// Get all registered trigger IDs
    pub fn list_triggers(&self) -> Vec<(String, TriggerPhase)> {
        self.registered_ids.read()
            .iter()
            .map(|(id, phase)| (id.clone(), *phase))
            .collect()
    }

    /// Check if a trigger is registered
    pub fn has_trigger(&self, id: &str) -> bool {
        self.registered_ids.read().contains_key(id)
    }

    /// Get trigger count by phase
    pub fn count_by_phase(&self, phase: TriggerPhase) -> usize {
        match phase {
            TriggerPhase::PreWrite => self.pre_write.read().len(),
            TriggerPhase::OnWrite => self.on_write.read().len(),
            TriggerPhase::PostWrite => self.post_write.read().len(),
        }
    }

    /// Get total trigger count
    pub fn total_count(&self) -> usize {
        self.registered_ids.read().len()
    }

    /// Sort triggers by priority
    fn sort_by_priority(&self, triggers: &mut Vec<TriggerEntry>) {
        triggers.sort_by_key(|e| e.priority());
    }
}

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

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

    struct TestTrigger {
        id: String,
        phase: TriggerPhase,
        should_abort: bool,
    }

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

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

        fn phase(&self) -> TriggerPhase {
            self.phase
        }

        fn execute(&self, _ctx: &TriggerContext) -> TriggerResult {
            if self.should_abort {
                TriggerResult::Abort("Test abort".to_string())
            } else {
                TriggerResult::Continue
            }
        }
    }

    #[test]
    fn test_register_trigger() {
        let registry = TriggerRegistry::new();
        let trigger = Arc::new(TestTrigger {
            id: "test-1".to_string(),
            phase: TriggerPhase::PreWrite,
            should_abort: false,
        });

        assert!(registry.register(trigger).is_ok());
        assert!(registry.has_trigger("test-1"));
        assert_eq!(registry.total_count(), 1);
    }

    #[test]
    fn test_duplicate_registration() {
        let registry = TriggerRegistry::new();
        let trigger1 = Arc::new(TestTrigger {
            id: "test-1".to_string(),
            phase: TriggerPhase::PreWrite,
            should_abort: false,
        });
        let trigger2 = Arc::new(TestTrigger {
            id: "test-1".to_string(),
            phase: TriggerPhase::PostWrite,
            should_abort: false,
        });

        assert!(registry.register(trigger1).is_ok());
        assert!(registry.register(trigger2).is_err());
    }

    #[test]
    fn test_unregister_trigger() {
        let registry = TriggerRegistry::new();
        let trigger = Arc::new(TestTrigger {
            id: "test-1".to_string(),
            phase: TriggerPhase::PreWrite,
            should_abort: false,
        });

        registry.register(trigger).unwrap();
        assert!(registry.unregister("test-1").is_ok());
        assert!(!registry.has_trigger("test-1"));
    }

    #[test]
    fn test_execute_phase() {
        let registry = TriggerRegistry::new();
        let trigger = Arc::new(TestTrigger {
            id: "test-1".to_string(),
            phase: TriggerPhase::PreWrite,
            should_abort: false,
        });

        registry.register(trigger).unwrap();

        let node = MemoryNode::new("test content");
        let mut ctx = TriggerContext::new(TriggerPhase::PreWrite, node);
        
        let result = registry.execute_phase(TriggerPhase::PreWrite, &mut ctx);
        assert!(result.should_continue());
    }

    #[test]
    fn test_abort_trigger() {
        let registry = TriggerRegistry::new();
        let trigger = Arc::new(TestTrigger {
            id: "test-abort".to_string(),
            phase: TriggerPhase::PreWrite,
            should_abort: true,
        });

        registry.register(trigger).unwrap();

        let node = MemoryNode::new("test content");
        let mut ctx = TriggerContext::new(TriggerPhase::PreWrite, node);
        
        let result = registry.execute_phase(TriggerPhase::PreWrite, &mut ctx);
        assert!(result.should_abort());
    }
}
