//! Lifecycle event emission and subscription.
//!
//! Provides an event bus for workspace lifecycle changes.

use std::sync::{Arc, Mutex};

use crate::lifecycle::{LifecycleEvent, WorkspaceState};
use tuppu_persistence::WorkspaceId;

// =============================================================================
// EventFilter
// =============================================================================

/// Filter for lifecycle events.
#[derive(Debug, Clone, Default)]
pub struct EventFilter {
    /// Only events for these workspaces (empty = all).
    pub workspaces: Vec<WorkspaceId>,
    /// Only events transitioning to these states (empty = all).
    pub to_states: Vec<WorkspaceState>,
    /// Only events transitioning from these states (empty = all).
    pub from_states: Vec<WorkspaceState>,
}

impl EventFilter {
    /// Create a filter that matches all events.
    pub fn all() -> Self {
        Self::default()
    }

    /// Create a filter for a specific workspace.
    pub fn for_workspace(id: WorkspaceId) -> Self {
        Self {
            workspaces: vec![id],
            ..Default::default()
        }
    }

    /// Create a filter for events entering a state.
    pub fn entering(state: WorkspaceState) -> Self {
        Self {
            to_states: vec![state],
            ..Default::default()
        }
    }

    /// Create a filter for events leaving a state.
    pub fn leaving(state: WorkspaceState) -> Self {
        Self {
            from_states: vec![state],
            ..Default::default()
        }
    }

    /// Check if an event matches this filter.
    pub fn matches(&self, event: &LifecycleEvent) -> bool {
        // Check workspace filter
        if !self.workspaces.is_empty() && !self.workspaces.contains(&event.workspace_id) {
            return false;
        }

        // Check to_states filter
        if !self.to_states.is_empty() && !self.to_states.contains(&event.to) {
            return false;
        }

        // Check from_states filter
        if !self.from_states.is_empty() && !self.from_states.contains(&event.from) {
            return false;
        }

        true
    }
}

// =============================================================================
// Subscriber
// =============================================================================

type SubscriberCallback = Box<dyn Fn(&LifecycleEvent) + Send + Sync>;

struct Subscriber {
    id: usize,
    filter: EventFilter,
    callback: SubscriberCallback,
}

// =============================================================================
// LifecycleEventBus
// =============================================================================

/// Event bus for lifecycle events.
pub struct LifecycleEventBus {
    subscribers: Arc<Mutex<Vec<Subscriber>>>,
    next_id: Arc<Mutex<usize>>,
    history: Arc<Mutex<Vec<LifecycleEvent>>>,
    history_limit: usize,
}

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

impl LifecycleEventBus {
    /// Create a new event bus.
    pub fn new() -> Self {
        Self {
            subscribers: Arc::new(Mutex::new(Vec::new())),
            next_id: Arc::new(Mutex::new(0)),
            history: Arc::new(Mutex::new(Vec::new())),
            history_limit: 1000,
        }
    }

    /// Create with a custom history limit.
    pub fn with_history_limit(limit: usize) -> Self {
        Self {
            history_limit: limit,
            ..Self::new()
        }
    }

    /// Subscribe to events with a filter.
    pub fn subscribe<F>(&self, filter: EventFilter, callback: F) -> SubscriptionHandle
    where
        F: Fn(&LifecycleEvent) + Send + Sync + 'static,
    {
        let mut next_id = self.next_id.lock().unwrap();
        let id = *next_id;
        *next_id += 1;

        let subscriber = Subscriber {
            id,
            filter,
            callback: Box::new(callback),
        };

        self.subscribers.lock().unwrap().push(subscriber);

        SubscriptionHandle {
            id,
            subscribers: Arc::clone(&self.subscribers),
        }
    }

    /// Subscribe to all events.
    pub fn subscribe_all<F>(&self, callback: F) -> SubscriptionHandle
    where
        F: Fn(&LifecycleEvent) + Send + Sync + 'static,
    {
        self.subscribe(EventFilter::all(), callback)
    }

    /// Emit an event to all matching subscribers.
    pub fn emit(&self, event: &LifecycleEvent) {
        // Add to history
        {
            let mut history = self.history.lock().unwrap();
            history.push(event.clone());
            while history.len() > self.history_limit {
                history.remove(0);
            }
        }

        // Notify subscribers
        let subscribers = self.subscribers.lock().unwrap();
        for sub in subscribers.iter() {
            if sub.filter.matches(event) {
                (sub.callback)(event);
            }
        }
    }

    /// Query event history.
    pub fn query_history(&self, filter: &EventFilter) -> Vec<LifecycleEvent> {
        let history = self.history.lock().unwrap();
        history.iter()
            .filter(|e| filter.matches(e))
            .cloned()
            .collect()
    }

    /// Get all history.
    pub fn history(&self) -> Vec<LifecycleEvent> {
        self.history.lock().unwrap().clone()
    }

    /// Clear history.
    pub fn clear_history(&self) {
        self.history.lock().unwrap().clear();
    }

    /// Get subscriber count.
    pub fn subscriber_count(&self) -> usize {
        self.subscribers.lock().unwrap().len()
    }
}

/// Handle to a subscription, used for unsubscribing.
pub struct SubscriptionHandle {
    id: usize,
    subscribers: Arc<Mutex<Vec<Subscriber>>>,
}

impl SubscriptionHandle {
    /// Unsubscribe from events.
    pub fn unsubscribe(self) {
        let mut subscribers = self.subscribers.lock().unwrap();
        subscribers.retain(|s| s.id != self.id);
    }
}

// =============================================================================
// Tests
// =============================================================================

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::atomic::{AtomicUsize, Ordering};

    fn make_event(from: WorkspaceState, to: WorkspaceState) -> LifecycleEvent {
        LifecycleEvent::new(WorkspaceId::new(), from, to)
    }

    #[test]
    fn event_emission() {
        let bus = LifecycleEventBus::new();
        let counter = Arc::new(AtomicUsize::new(0));
        let counter_clone = Arc::clone(&counter);

        bus.subscribe_all(move |_| {
            counter_clone.fetch_add(1, Ordering::SeqCst);
        });

        bus.emit(&make_event(WorkspaceState::Initializing, WorkspaceState::Active));
        
        assert_eq!(counter.load(Ordering::SeqCst), 1);
    }

    #[test]
    fn event_subscription() {
        let bus = LifecycleEventBus::new();
        let counter = Arc::new(AtomicUsize::new(0));
        let counter_clone = Arc::clone(&counter);

        let filter = EventFilter::entering(WorkspaceState::Active);
        bus.subscribe(filter, move |_| {
            counter_clone.fetch_add(1, Ordering::SeqCst);
        });

        // Should match
        bus.emit(&make_event(WorkspaceState::Initializing, WorkspaceState::Active));
        // Should not match
        bus.emit(&make_event(WorkspaceState::Active, WorkspaceState::Suspended));
        
        assert_eq!(counter.load(Ordering::SeqCst), 1);
    }

    #[test]
    fn multiple_subscribers() {
        let bus = LifecycleEventBus::new();
        let counter1 = Arc::new(AtomicUsize::new(0));
        let counter2 = Arc::new(AtomicUsize::new(0));
        
        let c1 = Arc::clone(&counter1);
        let c2 = Arc::clone(&counter2);

        bus.subscribe_all(move |_| { c1.fetch_add(1, Ordering::SeqCst); });
        bus.subscribe_all(move |_| { c2.fetch_add(10, Ordering::SeqCst); });

        bus.emit(&make_event(WorkspaceState::Active, WorkspaceState::Suspended));
        
        assert_eq!(counter1.load(Ordering::SeqCst), 1);
        assert_eq!(counter2.load(Ordering::SeqCst), 10);
    }

    #[test]
    fn event_with_reason() {
        let bus = LifecycleEventBus::new();
        let seen_reason = Arc::new(Mutex::new(None));
        let sr = Arc::clone(&seen_reason);

        bus.subscribe_all(move |e| {
            *sr.lock().unwrap() = e.reason.clone();
        });

        let event = LifecycleEvent::new(
            WorkspaceId::new(),
            WorkspaceState::Active,
            WorkspaceState::Disposed,
        ).with_reason("cleanup");

        bus.emit(&event);
        
        assert_eq!(*seen_reason.lock().unwrap(), Some("cleanup".to_string()));
    }

    #[test]
    fn event_filtering() {
        let ws_id = WorkspaceId::new();
        let filter = EventFilter::for_workspace(ws_id);

        let event1 = LifecycleEvent::new(ws_id, WorkspaceState::Active, WorkspaceState::Suspended);
        let event2 = LifecycleEvent::new(WorkspaceId::new(), WorkspaceState::Active, WorkspaceState::Suspended);

        assert!(filter.matches(&event1));
        assert!(!filter.matches(&event2));
    }

    #[test]
    fn event_history_query() {
        let bus = LifecycleEventBus::new();
        
        bus.emit(&make_event(WorkspaceState::Initializing, WorkspaceState::Active));
        bus.emit(&make_event(WorkspaceState::Active, WorkspaceState::Suspended));
        bus.emit(&make_event(WorkspaceState::Suspended, WorkspaceState::Active));

        let filter = EventFilter::entering(WorkspaceState::Active);
        let matches = bus.query_history(&filter);
        
        assert_eq!(matches.len(), 2);
    }

    #[test]
    fn event_ordering() {
        let bus = LifecycleEventBus::new();
        
        bus.emit(&make_event(WorkspaceState::Initializing, WorkspaceState::Active));
        std::thread::sleep(std::time::Duration::from_millis(10));
        bus.emit(&make_event(WorkspaceState::Active, WorkspaceState::Suspended));

        let history = bus.history();
        assert_eq!(history.len(), 2);
        assert!(history[0].timestamp <= history[1].timestamp);
    }

    #[test]
    fn unsubscribe() {
        let bus = LifecycleEventBus::new();
        let counter = Arc::new(AtomicUsize::new(0));
        let c = Arc::clone(&counter);

        let handle = bus.subscribe_all(move |_| {
            c.fetch_add(1, Ordering::SeqCst);
        });

        bus.emit(&make_event(WorkspaceState::Active, WorkspaceState::Suspended));
        assert_eq!(counter.load(Ordering::SeqCst), 1);

        handle.unsubscribe();

        bus.emit(&make_event(WorkspaceState::Suspended, WorkspaceState::Active));
        assert_eq!(counter.load(Ordering::SeqCst), 1); // Still 1, not 2
    }
}
