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

pub struct Event<T> {
    listeners: Mutex<Vec<Arc<dyn Fn(&T) + Send + Sync>>>,
}

impl<T> Debug for Event<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "Event({} listeners)",
            self.listeners.lock().unwrap().len()
        )
    }
}

impl<T> Default for Event<T> {
    fn default() -> Self {
        Self::new()
    }
}

impl<T> Event<T> {
    pub fn new() -> Self {
        Self {
            listeners: Mutex::new(Vec::new()),
        }
    }

    pub fn subscribe<F>(&self, f: F)
    where
        F: Fn(&T) + 'static + Send + Sync,
    {
        self.listeners.lock().unwrap().push(Arc::new(f));
    }

    pub fn emit(&self, data: &T) {
        // Clone the Arc pointers so we don't hold the lock while invoking callbacks
        let listeners = self.listeners.lock().unwrap().clone();
        for listener in listeners.iter() {
            listener(data);
        }
    }
}

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

    #[test]
    fn emit_with_no_listeners_should_not_panic() {
        let event: Event<i32> = Event::new();
        // Should not panic
        event.emit(&7);
        // Debug should reflect zero listeners
        assert_eq!(format!("{:?}", event), "Event(0 listeners)");
    }

    #[test]
    fn single_listener_receives_data() {
        let event: Event<i32> = Event::new();
        let received = Arc::new(Mutex::new(Vec::new()));
        let received_cloned = Arc::clone(&received);
        event.subscribe(move |v: &i32| {
            received_cloned.lock().unwrap().push(*v);
        });

        event.emit(&42);

        let out = received.lock().unwrap().clone();
        assert_eq!(out, vec![42]);
        // Debug should show one listener
        assert_eq!(format!("{:?}", event), "Event(1 listeners)");
    }

    #[test]
    fn multiple_listeners_called_in_subscription_order() {
        let event: Event<i32> = Event::new();
        let calls = Arc::new(Mutex::new(Vec::<u8>::new()));

        // First listener marks with 1
        let calls_l1 = Arc::clone(&calls);
        event.subscribe(move |_| {
            calls_l1.lock().unwrap().push(1);
        });

        // Second listener marks with 2
        let calls_l2 = Arc::clone(&calls);
        event.subscribe(move |_| {
            calls_l2.lock().unwrap().push(2);
        });

        event.emit(&0);

        let out = calls.lock().unwrap().clone();
        assert_eq!(out, vec![1, 2]);
        assert_eq!(format!("{:?}", event), "Event(2 listeners)");
    }

    #[test]
    fn subscribe_during_emit_takes_effect_next_time() {
        let event = Arc::new(Event::<i32>::new());
        let calls = Arc::new(Mutex::new(Vec::<&'static str>::new()));
        let did_subscribe = Arc::new(AtomicBool::new(false));

        // L1: On first call, subscribe L2; always record L1 call
        let event_for_l1 = Arc::clone(&event);
        let calls_for_l1 = Arc::clone(&calls);
        let flag_for_l1 = Arc::clone(&did_subscribe);
        event.subscribe(move |_| {
            calls_for_l1.lock().unwrap().push("L1");
            if flag_for_l1.swap(true, Ordering::SeqCst) == false {
                let calls_for_l2 = Arc::clone(&calls_for_l1);
                event_for_l1.subscribe(move |_| {
                    calls_for_l2.lock().unwrap().push("L2");
                });
            }
        });

        // First emit: only L1 should fire (L2 subscribed during this emit)
        event.emit(&0);
        assert_eq!(calls.lock().unwrap().clone(), vec!["L1"]);

        // Second emit: both L1 then L2 should fire in order
        event.emit(&0);
        assert_eq!(calls.lock().unwrap().clone(), vec!["L1", "L1", "L2"]);

        // Third emit: both continue firing; order preserved
        event.emit(&0);
        assert_eq!(
            calls.lock().unwrap().clone(),
            vec!["L1", "L1", "L2", "L1", "L2"]
        );

        // There should be exactly 2 listeners registered now
        assert_eq!(format!("{:?}", event), "Event(2 listeners)");
    }
}
