//! Selector-based event filtering emitter
//!
//! This emitter wraps another emitter and only passes through events that match
//! the given CSS-like selectors. It uses StackFrameCollector for efficient
//! selector matching and stack management.

use crate::events::bus_adapter::BusReaderIterator;
use crate::events::events::{Consumer, Emitter, Event};
use crate::parsers::selectors::StackFrameCollector;
use bus::Bus;
use std::sync::{Arc, Mutex};
use std::thread;

/// Consumer that filters events based on selectors using StackFrameCollector
struct SelectorFilterConsumer {
    collector: StackFrameCollector,
    output_bus: Arc<Mutex<Bus<Event>>>,
    /// Track whether we're currently inside a matching block (including its children)
    matching_depth: Option<usize>,
}

impl Consumer for SelectorFilterConsumer {
    fn handle(&mut self, event: Event) {
        match &event {
            Event::BlockStarted { .. } => {
                // Update the collector
                self.collector.handle_event(&event);

                // Check if current state matches any selector
                let matches = self.collector.matches_any_selector();

                // If we're not currently in a matching block and this matches, start outputting
                if matches && self.matching_depth.is_none() {
                    self.matching_depth = Some(self.collector.depth() - 1);
                }

                // If we're in a matching block (or just started one), emit the event
                if self.matching_depth.is_some() {
                    if let Ok(mut bus) = self.output_bus.lock() {
                        bus.broadcast(event.clone());
                    }
                }
            }
            Event::BlockFinished => {
                // If we're in a matching block, emit the event
                if self.matching_depth.is_some() {
                    if let Ok(mut bus) = self.output_bus.lock() {
                        bus.broadcast(event.clone());
                    }
                }

                // Check if we're finishing the matching block
                if let Some(depth) = self.matching_depth {
                    if self.collector.depth() - 1 == depth {
                        self.matching_depth = None;
                    }
                }

                // Update the collector
                self.collector.handle_event(&event);
            }
            Event::TextFinished { .. } => {
                // Collector handles text events (no stack changes)
                self.collector.handle_event(&event);

                // If we're in a matching block, emit the text
                if self.matching_depth.is_some() {
                    if let Ok(mut bus) = self.output_bus.lock() {
                        bus.broadcast(event.clone());
                    }
                }
            }
            Event::ContextLine { .. } => {
                // Collector handles context line events (no stack changes)
                self.collector.handle_event(&event);

                // If we're in a matching block, emit the context line
                if self.matching_depth.is_some() {
                    if let Ok(mut bus) = self.output_bus.lock() {
                        bus.broadcast(event.clone());
                    }
                }
            }
            Event::VoidElement { .. } => {
                // Collector handles void element events (no stack changes - self-closing)
                self.collector.handle_event(&event);

                // If we're in a matching block, emit the void element
                if self.matching_depth.is_some() {
                    if let Ok(mut bus) = self.output_bus.lock() {
                        bus.broadcast(event.clone());
                    }
                }
            }
        }
    }
}

/// Emitter that filters events from another emitter based on selectors
pub struct SelectorEmitter<E: Emitter> {
    inner_emitter: E,
    selectors: Vec<String>,
    bus: Arc<Mutex<Bus<Event>>>,
}

impl<E: Emitter> SelectorEmitter<E> {
    pub fn new(inner_emitter: E, selectors: Vec<String>) -> Self {
        Self {
            inner_emitter,
            selectors,
            bus: Arc::new(Mutex::new(Bus::new(1024))),
        }
    }
}

impl<E: Emitter + 'static> Emitter for SelectorEmitter<E> {
    type Reader = BusReaderIterator;

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

    fn run(self) {
        let bus = self.bus.clone();
        let selectors = self.selectors.clone();

        // Subscribe to inner emitter
        let inner_reader = self.inner_emitter.subscribe();

        // Create filter consumer
        let mut filter = SelectorFilterConsumer {
            collector: StackFrameCollector::new(selectors),
            output_bus: bus,
            matching_depth: None,
        };

        // Start inner emitter in separate thread
        thread::spawn(move || {
            self.inner_emitter.run();
        });

        // Process events from inner emitter
        filter.run(inner_reader);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::events::events::Event;
    use crate::parsers::wasot::arguments::ParsedArguments;
    use crate::parsers::wasot::parser::{Id, MetaLine, QuotedString};

    #[test]
    fn test_selector_emitter_creation() {
        use crate::emitters::wasot_file_emitter::WasotFileEmitter;
        use std::io::Cursor;

        let input = ".paragraph\nHello world\n";
        let cursor = Cursor::new(input.as_bytes());
        let inner_emitter = WasotFileEmitter::from_reader(Box::new(cursor));

        let selectors = vec!["paragraph".to_string()];
        let emitter = SelectorEmitter::new(inner_emitter, selectors);

        // Just verify it can be created without panicking
        let _reader = emitter.subscribe();
    }

    #[test]
    fn test_selector_filter_consumer_creation() {
        let selectors = vec!["paragraph".to_string(), "section > paragraph".to_string()];
        let collector = StackFrameCollector::new(selectors);

        // Verify collector can be created successfully
        assert_eq!(collector.selector_count(), 2);
    }

    #[test]
    fn test_selector_filter_consumer_handle_block_started_matching() {
        let selectors = vec!["paragraph".to_string()];
        let bus = Arc::new(Mutex::new(Bus::new(1024)));
        let mut consumer = SelectorFilterConsumer {
            collector: StackFrameCollector::new(selectors),
            output_bus: bus.clone(),
            matching_depth: None,
        };

        // Create a matching block started event
        let meta_line = MetaLine {
            level: 1,
            type_name: "paragraph".to_string(),
            id: Id::empty(),
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            inline_text: QuotedString::new("".to_string(), false),
            line_index: Some(1),
            is_void: false,
        };
        let event = Event::BlockStarted { meta_line };

        // Handle the event
        consumer.handle(event);

        // Check that matching_depth is set
        assert!(consumer.matching_depth.is_some());
    }

    #[test]
    fn test_selector_filter_consumer_handle_block_started_non_matching() {
        let selectors = vec!["paragraph".to_string()];
        let bus = Arc::new(Mutex::new(Bus::new(1024)));
        let mut consumer = SelectorFilterConsumer {
            collector: StackFrameCollector::new(selectors),
            output_bus: bus.clone(),
            matching_depth: None,
        };

        // Create a non-matching block started event
        let meta_line = MetaLine {
            level: 1,
            type_name: "section".to_string(),
            id: Id::empty(),
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            inline_text: QuotedString::new("".to_string(), false),
            line_index: Some(1),
            is_void: false,
        };
        let event = Event::BlockStarted { meta_line };

        // Handle the event
        consumer.handle(event);

        // Check that matching_depth remains None
        assert!(consumer.matching_depth.is_none());
    }

    #[test]
    fn test_selector_filter_consumer_handle_block_finished_matching() {
        let selectors = vec!["paragraph".to_string()];
        let bus = Arc::new(Mutex::new(Bus::new(1024)));

        // First, start a matching block to get into a valid state
        let mut consumer = SelectorFilterConsumer {
            collector: StackFrameCollector::new(selectors),
            output_bus: bus.clone(),
            matching_depth: None,
        };

        // Create and handle a block started event to get into matching state
        let meta_line = MetaLine {
            level: 1,
            type_name: "paragraph".to_string(),
            id: Id::empty(),
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            inline_text: QuotedString::new("".to_string(), false),
            line_index: Some(1),
            is_void: false,
        };
        let start_event = Event::BlockStarted { meta_line };
        consumer.handle(start_event);

        // Verify we're now in a matching state
        assert!(consumer.matching_depth.is_some());

        // Now handle a block finished event
        let event = Event::BlockFinished;
        consumer.handle(event);

        // matching_depth should now be None since we finished the matching block
        assert!(consumer.matching_depth.is_none());
    }

    #[test]
    fn test_selector_filter_consumer_handle_text_finished_matching() {
        let selectors = vec!["paragraph".to_string()];
        let bus = Arc::new(Mutex::new(Bus::new(1024)));
        let mut consumer = SelectorFilterConsumer {
            collector: StackFrameCollector::new(selectors),
            output_bus: bus.clone(),
            matching_depth: Some(0), // Already in a matching block
        };

        // Create a text finished event
        let event = Event::TextFinished {
            lines: vec!["Hello world".to_string()],
            links: vec![vec![]],
            footnotes: vec![],
        };

        // Handle the event - should broadcast since we're in a matching block
        consumer.handle(event);

        // Should still be in matching state
        assert!(consumer.matching_depth.is_some());
    }

    #[test]
    fn test_selector_filter_consumer_handle_text_finished_non_matching() {
        let selectors = vec!["paragraph".to_string()];
        let bus = Arc::new(Mutex::new(Bus::new(1024)));
        let mut consumer = SelectorFilterConsumer {
            collector: StackFrameCollector::new(selectors),
            output_bus: bus.clone(),
            matching_depth: None, // Not in a matching block
        };

        // Create a text finished event
        let event = Event::TextFinished {
            lines: vec!["Hello world".to_string()],
            links: vec![vec![]],
            footnotes: vec![],
        };

        // Handle the event - should not broadcast since we're not in a matching block
        consumer.handle(event);

        // Should still not be in matching state
        assert!(consumer.matching_depth.is_none());
    }

    #[test]
    fn test_selector_emitter_new() {
        use crate::emitters::wasot_file_emitter::WasotFileEmitter;
        use std::io::Cursor;

        let input = ".paragraph\nHello world\n";
        let cursor = Cursor::new(input.as_bytes());
        let inner_emitter = WasotFileEmitter::from_reader(Box::new(cursor));

        let selectors = vec!["paragraph".to_string(), "section".to_string()];
        let emitter = SelectorEmitter::new(inner_emitter, selectors.clone());

        // Verify the selectors are stored correctly
        assert_eq!(emitter.selectors, selectors);
    }

    #[test]
    fn test_selector_emitter_subscribe() {
        use crate::emitters::wasot_file_emitter::WasotFileEmitter;
        use std::io::Cursor;

        let input = ".paragraph\nHello world\n";
        let cursor = Cursor::new(input.as_bytes());
        let inner_emitter = WasotFileEmitter::from_reader(Box::new(cursor));

        let selectors = vec!["paragraph".to_string()];
        let emitter = SelectorEmitter::new(inner_emitter, selectors);

        // Test that subscribe returns a BusReaderIterator
        let reader = emitter.subscribe();

        // Just verify we got a reader without panicking
        // The reader type should implement Iterator<Item = Event>
        drop(reader);
    }
}
