use crate::events::{Consumer, Emitter};
use std::thread;

/// Generic function to run any emitter with a single consumer
///
/// This function demonstrates the core emitter-consumer pattern:
/// 1. Takes an emitter that implements the Emitter trait
/// 2. Takes a consumer that implements the Consumer trait
/// 3. Sets up the channel between them
/// 4. Runs them together and handles the results
pub fn run<E, C>(emitter: E, consumer: C) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
where
    E: Emitter + 'static,
    C: Consumer + 'static,
{
    // Get the reader from the emitter
    let reader = emitter.subscribe();

    // Start the emitter in a separate thread
    let emitter_handle = thread::spawn(move || {
        emitter.run();
    });

    // Start the consumer in a separate thread
    let consumer_handle = thread::spawn(move || {
        let mut consumer = consumer;
        consumer.run(reader);
    });

    // Wait for consumer to complete
    consumer_handle
        .join()
        .map_err(|_| "Consumer thread panicked")?;

    // Wait for emitter to complete
    emitter_handle
        .join()
        .map_err(|_| "Emitter thread panicked")?;

    Ok(())
}

/// Generic function to run any emitter with two consumers
///
/// This demonstrates running multiple consumers with the same emitter
/// without requiring dyn trait objects.
pub fn run_dual<E, C1, C2>(
    emitter: E,
    consumer1: C1,
    consumer2: C2,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
where
    E: Emitter + 'static,
    C1: Consumer + 'static,
    C2: Consumer + 'static,
{
    // Get readers for each consumer
    let reader1 = emitter.subscribe();
    let reader2 = emitter.subscribe();

    // Start the emitter in a separate thread
    let emitter_handle = thread::spawn(move || {
        emitter.run();
    });

    // Start consumers in separate threads
    let consumer1_handle = thread::spawn(move || {
        let mut consumer1 = consumer1;
        consumer1.run(reader1);
    });

    let consumer2_handle = thread::spawn(move || {
        let mut consumer2 = consumer2;
        consumer2.run(reader2);
    });

    // Wait for all consumers to complete
    consumer1_handle
        .join()
        .map_err(|_| "Consumer 1 thread panicked")?;

    consumer2_handle
        .join()
        .map_err(|_| "Consumer 2 thread panicked")?;

    // Wait for emitter to complete
    emitter_handle
        .join()
        .map_err(|_| "Emitter thread panicked")?;

    Ok(())
}

/// Generic function to run any emitter with three consumers
///
/// This demonstrates running multiple consumers with the same emitter
/// without requiring dyn trait objects.
pub fn run_triple<E, C1, C2, C3>(
    emitter: E,
    consumer1: C1,
    consumer2: C2,
    consumer3: C3,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
where
    E: Emitter + 'static,
    C1: Consumer + 'static,
    C2: Consumer + 'static,
    C3: Consumer + 'static,
{
    // Get readers for each consumer
    let reader1 = emitter.subscribe();
    let reader2 = emitter.subscribe();
    let reader3 = emitter.subscribe();

    // Start the emitter in a separate thread
    let emitter_handle = thread::spawn(move || {
        emitter.run();
    });

    // Start consumers in separate threads
    let consumer1_handle = thread::spawn(move || {
        let mut consumer1 = consumer1;
        consumer1.run(reader1);
    });

    let consumer2_handle = thread::spawn(move || {
        let mut consumer2 = consumer2;
        consumer2.run(reader2);
    });

    let consumer3_handle = thread::spawn(move || {
        let mut consumer3 = consumer3;
        consumer3.run(reader3);
    });

    // Wait for all consumers to complete
    consumer1_handle
        .join()
        .map_err(|_| "Consumer 1 thread panicked")?;

    consumer2_handle
        .join()
        .map_err(|_| "Consumer 2 thread panicked")?;

    consumer3_handle
        .join()
        .map_err(|_| "Consumer 3 thread panicked")?;

    // Wait for emitter to complete
    emitter_handle
        .join()
        .map_err(|_| "Emitter thread panicked")?;

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::events::{BusReaderIterator, Event};
    use crate::parsers::wasot::{Id, MetaLine, ParsedArguments, QuotedString};
    use bus::Bus;
    use std::sync::{Arc, Mutex};
    use std::time::Duration;

    /// Mock emitter for testing
    struct MockEmitter {
        bus: Arc<Mutex<Bus<Event>>>,
        events: Vec<Event>,
        should_panic: bool,
        delay_ms: Option<u64>,
    }

    impl MockEmitter {
        fn new(events: Vec<Event>) -> Self {
            Self {
                bus: Arc::new(Mutex::new(Bus::new(100))),
                events,
                should_panic: false,
                delay_ms: None,
            }
        }

        fn with_panic() -> Self {
            Self {
                bus: Arc::new(Mutex::new(Bus::new(100))),
                events: vec![],
                should_panic: true,
                delay_ms: None,
            }
        }

        fn with_delay(events: Vec<Event>, delay_ms: u64) -> Self {
            Self {
                bus: Arc::new(Mutex::new(Bus::new(100))),
                events,
                should_panic: false,
                delay_ms: Some(delay_ms),
            }
        }
    }

    impl Emitter for MockEmitter {
        type Reader = BusReaderIterator;

        fn subscribe(&self) -> Self::Reader {
            let reader = self.bus.lock().unwrap().add_rx();
            BusReaderIterator::new(reader)
        }

        fn run(self) {
            if self.should_panic {
                panic!("Emitter panic test");
            }

            for event in self.events {
                if let Some(delay) = self.delay_ms {
                    thread::sleep(Duration::from_millis(delay));
                }
                if let Ok(mut bus) = self.bus.lock() {
                    bus.broadcast(event);
                }
            }
        }
    }

    /// Mock consumer that collects events
    struct CollectingConsumer {
        collected: Arc<Mutex<Vec<Event>>>,
    }

    impl CollectingConsumer {
        fn new() -> Self {
            Self {
                collected: Arc::new(Mutex::new(Vec::new())),
            }
        }
    }

    impl Consumer for CollectingConsumer {
        fn handle(&mut self, event: Event) {
            self.collected.lock().unwrap().push(event);
        }
    }

    /// Mock consumer that counts events
    struct CountingConsumer {
        count: Arc<Mutex<usize>>,
    }

    impl CountingConsumer {
        fn new(_id: usize) -> Self {
            Self {
                count: Arc::new(Mutex::new(0)),
            }
        }
    }

    impl Consumer for CountingConsumer {
        fn handle(&mut self, _event: Event) {
            *self.count.lock().unwrap() += 1;
        }
    }

    /// Mock consumer that errors after a certain number of events
    struct ErroringConsumer {
        error_after: usize,
    }

    impl ErroringConsumer {
        fn new(error_after: usize) -> Self {
            Self { error_after }
        }
    }

    impl Consumer for ErroringConsumer {
        fn handle(&mut self, _event: Event) {
            if self.error_after == 0 {
                panic!("Consumer error test");
            }
            self.error_after -= 1;
        }
    }

    fn create_test_events() -> Vec<Event> {
        vec![
            Event::BlockStarted {
                meta_line: MetaLine {
                    level: 1,
                    type_name: "test".to_string(),
                    arguments: ParsedArguments {
                        args: vec![],
                        kwargs: vec![],
                    },
                    id: Id {
                        value: String::new(),
                    },
                    line_index: Some(0),
                    is_void: false,
                    inline_text: QuotedString::new(String::new(), false),
                },
            },
            Event::TextFinished {
                lines: vec!["Test content".to_string()],
                links: vec![Vec::new()],
                footnotes: vec![],
            },
            Event::BlockFinished,
        ]
    }

    #[test]
    fn test_run_successful() {
        let events = create_test_events();
        let emitter = MockEmitter::new(events.clone());
        let consumer = CollectingConsumer::new();
        let consumer_clone = consumer.collected.clone();

        let result = run(emitter, consumer);
        assert!(result.is_ok());

        // Give threads a moment to complete
        thread::sleep(Duration::from_millis(10));

        let collected = consumer_clone.lock().unwrap().clone();
        assert_eq!(collected.len(), 3);
        assert_eq!(collected, events);
    }

    #[test]
    fn test_run_emitter_panic() {
        let emitter = MockEmitter::with_panic();
        let consumer = CollectingConsumer::new();

        let result = run(emitter, consumer);
        assert!(result.is_err());
        assert!(result
            .unwrap_err()
            .to_string()
            .contains("Emitter thread panicked"));
    }

    #[test]
    fn test_run_consumer_error() {
        let events = create_test_events();
        let emitter = MockEmitter::new(events);
        let consumer = ErroringConsumer::new(1);

        let result = run(emitter, consumer);
        assert!(result.is_err());
        assert!(result
            .unwrap_err()
            .to_string()
            .contains("Consumer thread panicked"));
    }

    #[test]
    fn test_run_dual_successful() {
        let events = create_test_events();
        let emitter = MockEmitter::new(events.clone());

        let consumer1 = CountingConsumer::new(1);
        let consumer2 = CountingConsumer::new(2);

        // Keep references to check counts later
        let count1 = consumer1.count.clone();
        let count2 = consumer2.count.clone();

        let result = run_dual(emitter, consumer1, consumer2);
        assert!(result.is_ok());

        // Give threads a moment to complete
        thread::sleep(Duration::from_millis(10));

        // Each consumer should have received all events
        assert_eq!(*count1.lock().unwrap(), 3);
        assert_eq!(*count2.lock().unwrap(), 3);
    }

    #[test]
    fn test_run_triple_successful() {
        let events = create_test_events();
        let emitter = MockEmitter::new(events.clone());

        let consumer1 = CountingConsumer::new(1);
        let consumer2 = CountingConsumer::new(2);
        let consumer3 = CountingConsumer::new(3);

        // Keep references to check counts later
        let count1 = consumer1.count.clone();
        let count2 = consumer2.count.clone();
        let count3 = consumer3.count.clone();

        let result = run_triple(emitter, consumer1, consumer2, consumer3);
        assert!(result.is_ok());

        // Give threads a moment to complete
        thread::sleep(Duration::from_millis(10));

        // Each consumer should have received all events
        assert_eq!(*count1.lock().unwrap(), 3);
        assert_eq!(*count2.lock().unwrap(), 3);
        assert_eq!(*count3.lock().unwrap(), 3);
    }

    #[test]
    fn test_run_dual_with_failing_consumer() {
        let events = create_test_events();
        let emitter = MockEmitter::new(events);

        let consumer1 = CountingConsumer::new(1);
        let consumer2 = ErroringConsumer::new(1);

        let count1 = consumer1.count.clone();

        let result = run_dual(emitter, consumer1, consumer2);
        assert!(result.is_err());
        assert!(result
            .unwrap_err()
            .to_string()
            .contains("Consumer 2 thread panicked"));

        // Give threads a moment to complete
        thread::sleep(Duration::from_millis(10));

        // The successful consumer should still have processed events
        assert!(*count1.lock().unwrap() > 0);
    }

    #[test]
    fn test_run_with_empty_events() {
        let emitter = MockEmitter::new(vec![]);
        let consumer = CollectingConsumer::new();
        let consumer_clone = consumer.collected.clone();

        let result = run(emitter, consumer);
        assert!(result.is_ok());

        // Give threads a moment to complete
        thread::sleep(Duration::from_millis(10));

        let collected = consumer_clone.lock().unwrap().clone();
        assert_eq!(collected.len(), 0);
    }

    #[test]
    fn test_run_dual_concurrent_processing() {
        let events = vec![
            Event::TextFinished {
                lines: vec!["1".to_string()],
                links: vec![Vec::new()],
                footnotes: vec![],
            },
            Event::TextFinished {
                lines: vec!["2".to_string()],
                links: vec![Vec::new()],
                footnotes: vec![],
            },
            Event::TextFinished {
                lines: vec!["3".to_string()],
                links: vec![Vec::new()],
                footnotes: vec![],
            },
            Event::TextFinished {
                lines: vec!["4".to_string()],
                links: vec![Vec::new()],
                footnotes: vec![],
            },
            Event::TextFinished {
                lines: vec!["5".to_string()],
                links: vec![Vec::new()],
                footnotes: vec![],
            },
        ];

        let emitter = MockEmitter::with_delay(events.clone(), 1);

        let consumer1 = CountingConsumer::new(1);
        let consumer2 = CountingConsumer::new(2);

        let count1 = consumer1.count.clone();
        let count2 = consumer2.count.clone();

        let result = run_dual(emitter, consumer1, consumer2);
        assert!(result.is_ok());

        // Give threads a moment to complete
        thread::sleep(Duration::from_millis(50));

        // Both consumers should have received all events
        assert_eq!(*count1.lock().unwrap(), 5);
        assert_eq!(*count2.lock().unwrap(), 5);
    }
}
