use crate::events::{Emitter, Event};
use crate::parsers::wasot::{Id, MetaLine, ParsedArguments, QuotedString};
use bus::Bus;
use serde::{Deserialize, Deserializer, Serialize};
use std::fs::File;
use std::io::{self, BufRead, BufReader, Read};
use std::path::Path;
use std::sync::{Arc, Mutex};
use std::thread;

/// A quoted string in JSONL format
/// Can be deserialized from either a plain string or an object with content/quoted fields
#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct JsonlQuotedString {
    pub content: String,
    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
    pub quoted: bool,
}

impl<'de> Deserialize<'de> for JsonlQuotedString {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        use serde::de::{self, MapAccess, Visitor};

        struct JsonlQuotedStringVisitor;

        impl<'de> Visitor<'de> for JsonlQuotedStringVisitor {
            type Value = JsonlQuotedString;

            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("a string or an object with 'content' field")
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
            where
                E: de::Error,
            {
                Ok(JsonlQuotedString {
                    content: value.to_string(),
                    quoted: false,
                })
            }

            fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
            where
                E: de::Error,
            {
                Ok(JsonlQuotedString {
                    content: value,
                    quoted: false,
                })
            }

            fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
            where
                M: MapAccess<'de>,
            {
                let mut content = None;
                let mut quoted = false;

                while let Some(key) = map.next_key::<String>()? {
                    match key.as_str() {
                        "content" => {
                            content = Some(map.next_value()?);
                        }
                        "quoted" => {
                            quoted = map.next_value()?;
                        }
                        _ => {
                            let _ = map.next_value::<serde::de::IgnoredAny>()?;
                        }
                    }
                }

                let content = content.ok_or_else(|| de::Error::missing_field("content"))?;
                Ok(JsonlQuotedString { content, quoted })
            }
        }

        deserializer.deserialize_any(JsonlQuotedStringVisitor)
    }
}

impl From<&str> for JsonlQuotedString {
    fn from(s: &str) -> Self {
        Self {
            content: s.to_string(),
            quoted: false,
        }
    }
}

impl From<String> for JsonlQuotedString {
    fn from(s: String) -> Self {
        Self {
            content: s,
            quoted: false,
        }
    }
}

/// A block in JSONL format
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonlBlock {
    pub block_type: String,
    pub id: Option<String>,
    #[serde(default = "default_level", skip_serializing_if = "is_default_level")]
    pub level: usize,
    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
    pub is_void: bool,
    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
    pub is_context_line: bool,
    pub args: Vec<JsonlQuotedString>,
    pub kwargs: Vec<(JsonlQuotedString, JsonlQuotedString)>,
    pub inline_text: Option<JsonlQuotedString>,
    pub content: Vec<JsonlElement>,
}

fn default_level() -> usize {
    1
}

fn is_default_level(level: &usize) -> bool {
    *level == 1
}

/// An element in JSONL format (either a block or text)
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum JsonlElement {
    Block(JsonlBlock),
    Text { lines: Vec<String> },
}

/// Emitter that reads JSONL format and produces Events
pub struct JsonlEmitter {
    bus: Arc<Mutex<Bus<Event>>>,
    file_path: Option<String>,
    use_stdin: bool,
}

impl JsonlEmitter {
    /// Create a new JSONL emitter from a file path
    pub fn from_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
        let file_path = path.as_ref().to_string_lossy().into_owned();
        let bus = Arc::new(Mutex::new(Bus::new(1000)));

        Ok(Self {
            bus,
            file_path: Some(file_path),
            use_stdin: false,
        })
    }

    /// Create a new JSONL emitter from stdin
    pub fn from_stdin() -> Self {
        let bus = Arc::new(Mutex::new(Bus::new(1000)));

        Self {
            bus,
            file_path: None,
            use_stdin: true,
        }
    }

    /// Parse JSONL and emit events
    fn process_jsonl(&self, reader: Box<dyn Read + Send>) -> io::Result<()> {
        let reader = BufReader::new(reader);
        let mut line_index = 0;

        for line in reader.lines() {
            let line = line?;
            line_index += 1;

            // Skip empty lines
            if line.trim().is_empty() {
                continue;
            }

            // Parse the JSON line
            let element: JsonlElement = serde_json::from_str(&line)
                .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;

            self.process_element(element, &mut line_index, 1)?;
        }

        Ok(())
    }

    /// Process a JSONL element and emit corresponding events
    fn process_element(
        &self,
        element: JsonlElement,
        line_index: &mut usize,
        _depth: usize,
    ) -> io::Result<()> {
        match element {
            JsonlElement::Block(block) => {
                // Convert args and kwargs to ParsedArguments
                let args = block
                    .args
                    .into_iter()
                    .map(|arg| QuotedString::new(arg.content, arg.quoted))
                    .collect();
                let kwargs = block
                    .kwargs
                    .into_iter()
                    .map(|(k, v)| {
                        (
                            QuotedString::new(k.content, k.quoted),
                            QuotedString::new(v.content, v.quoted),
                        )
                    })
                    .collect();

                let arguments = ParsedArguments { args, kwargs };

                // Create MetaLine from block data, using the stored level
                let inline_text = block.inline_text.unwrap_or(JsonlQuotedString {
                    content: String::new(),
                    quoted: false,
                });
                let meta_line = MetaLine {
                    type_name: block.block_type.clone(),
                    inline_text: QuotedString::new(inline_text.content, inline_text.quoted),
                    level: block.level,
                    id: Id::new(block.id.unwrap_or_default()),
                    line_index: Some(*line_index),
                    arguments,
                    is_void: block.is_void,
                };

                // Check if this is a context line
                if block.is_context_line || (block.block_type == "id" && block.content.is_empty()) {
                    // Emit ContextLine event for context lines
                    self.bus
                        .lock()
                        .unwrap()
                        .broadcast(Event::ContextLine { meta_line });
                } else if block.is_void {
                    // Emit VoidElement event for void elements (self-closing blocks)
                    self.bus
                        .lock()
                        .unwrap()
                        .broadcast(Event::VoidElement { meta_line });
                } else {
                    // Emit BlockStarted event for regular blocks
                    self.bus
                        .lock()
                        .unwrap()
                        .broadcast(Event::BlockStarted { meta_line });

                    // Process nested content with incremented depth
                    for child in block.content {
                        self.process_element(child, line_index, _depth + 1)?;
                    }

                    // Emit BlockFinished event
                    self.bus.lock().unwrap().broadcast(Event::BlockFinished);
                }
            }
            JsonlElement::Text { lines } => {
                if !lines.is_empty() {
                    // Parse links from each text line
                    let links: Vec<Vec<crate::parsers::wasot::Link>> = lines
                        .iter()
                        .map(|line| {
                            use crate::parsers::wasot::{detect_links, parse_link};
                            let spans = detect_links(line);
                            spans
                                .into_iter()
                                .map(|span| {
                                    parse_link(&span.content, span.start_index, span.length)
                                })
                                .collect()
                        })
                        .collect();

                    self.bus.lock().unwrap().broadcast(Event::TextFinished {
                        lines,
                        links,
                        footnotes: vec![],
                    });
                }
            }
        }

        Ok(())
    }
}

impl Emitter for JsonlEmitter {
    type Reader = crate::events::BusReaderIterator;

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

    fn run(self) {
        let file_path = self.file_path.clone();
        let use_stdin = self.use_stdin;

        thread::spawn(move || {
            let reader: Box<dyn Read + Send> = if use_stdin {
                Box::new(io::stdin())
            } else if let Some(path) = file_path {
                match File::open(&path) {
                    Ok(file) => Box::new(file),
                    Err(e) => {
                        eprintln!("Error opening file {path}: {e}");
                        return;
                    }
                }
            } else {
                eprintln!("No input source specified");
                return;
            };

            if let Err(e) = self.process_jsonl(reader) {
                eprintln!("Error processing JSONL: {e}");
            }
            // Bus will be dropped when thread exits
        });
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::events::Event;
    use std::io::Cursor;
    use std::io::Write;
    use std::thread;
    use tempfile::NamedTempFile;

    #[test]
    fn test_from_file() {
        let mut temp_file = NamedTempFile::new().unwrap();
        write!(temp_file, "test content").unwrap();

        let emitter = JsonlEmitter::from_file(temp_file.path()).unwrap();
        assert!(emitter.file_path.is_some());
        assert!(!emitter.use_stdin);
        assert!(emitter
            .file_path
            .as_ref()
            .unwrap()
            .ends_with(temp_file.path().file_name().unwrap().to_str().unwrap()));
    }

    #[test]
    fn test_create_from_stdin() {
        let emitter = JsonlEmitter::from_stdin();
        assert!(emitter.file_path.is_none());
        assert!(emitter.use_stdin);
    }

    #[test]
    fn test_create_from_file() {
        let emitter = JsonlEmitter::from_file("/tmp/test.jsonl").unwrap();
        assert_eq!(emitter.file_path.as_ref().unwrap(), "/tmp/test.jsonl");
        assert!(!emitter.use_stdin);
    }

    #[test]
    fn test_process_empty_jsonl() {
        let emitter = JsonlEmitter::from_stdin();
        let reader = Box::new(Cursor::new(b""));
        let result = emitter.process_jsonl(reader);
        assert!(result.is_ok());
    }

    #[test]
    fn test_process_jsonl_with_empty_lines() {
        let emitter = JsonlEmitter::from_stdin();
        let jsonl_content = "\n\n   \n";
        let reader = Box::new(Cursor::new(jsonl_content.as_bytes()));
        let result = emitter.process_jsonl(reader);
        assert!(result.is_ok());
    }

    #[test]
    fn test_process_jsonl_with_text_element() {
        let emitter = JsonlEmitter::from_stdin();
        let jsonl_content = r#"{"type":"text","lines":["Hello world","Second line"]}"#;
        let reader = Box::new(Cursor::new(jsonl_content.as_bytes()));
        let result = emitter.process_jsonl(reader);
        assert!(result.is_ok());
    }

    #[test]
    fn test_process_jsonl_with_block_element() {
        let emitter = JsonlEmitter::from_stdin();
        let jsonl_content = r#"{"type":"block","block_type":"paragraph","id":"test123","args":["arg1"],"kwargs":[["key1","value1"]],"inline_text":"inline text","content":[]}"#;
        let reader = Box::new(Cursor::new(jsonl_content.as_bytes()));
        let result = emitter.process_jsonl(reader);
        assert!(result.is_ok());
    }

    #[test]
    fn test_process_jsonl_with_nested_block() {
        let emitter = JsonlEmitter::from_stdin();
        let jsonl_content = r#"{"type":"block","block_type":"section","id":"main","args":[],"kwargs":[],"inline_text":"","content":[{"type":"text","lines":["Nested text"]}]}"#;
        let reader = Box::new(Cursor::new(jsonl_content.as_bytes()));
        let result = emitter.process_jsonl(reader);
        assert!(result.is_ok());
    }

    #[test]
    fn test_process_jsonl_invalid_json() {
        let emitter = JsonlEmitter::from_stdin();
        let jsonl_content = "invalid json";
        let reader = Box::new(Cursor::new(jsonl_content.as_bytes()));
        let result = emitter.process_jsonl(reader);
        assert!(result.is_err());
    }

    #[test]
    fn test_process_element_text() {
        let emitter = JsonlEmitter::from_stdin();
        let element = JsonlElement::Text {
            lines: vec!["Hello".to_string(), "World".to_string()],
        };
        let mut line_index = 1;
        let result = emitter.process_element(element, &mut line_index, 1);
        assert!(result.is_ok());
    }

    #[test]
    fn test_process_element_text_empty() {
        let emitter = JsonlEmitter::from_stdin();
        let element = JsonlElement::Text { lines: vec![] };
        let mut line_index = 1;
        let result = emitter.process_element(element, &mut line_index, 1);
        assert!(result.is_ok());
    }

    #[test]
    fn test_process_element_block_with_args() {
        let emitter = JsonlEmitter::from_stdin();
        let block = JsonlBlock {
            block_type: "paragraph".to_string(),
            id: Some("test123".to_string()),
            level: 1,
            is_void: false,
            is_context_line: false,
            args: vec!["arg1".into(), "arg2".into()],
            kwargs: vec![("key1".into(), "value1".into())],
            inline_text: Some("inline text".into()),
            content: vec![],
        };
        let element = JsonlElement::Block(block);
        let mut line_index = 1;
        let result = emitter.process_element(element, &mut line_index, 1);
        assert!(result.is_ok());
    }

    #[test]
    fn test_process_element_block_minimal() {
        let emitter = JsonlEmitter::from_stdin();
        let block = JsonlBlock {
            block_type: "div".to_string(),
            id: None,
            level: 1,
            is_void: false,
            is_context_line: false,
            args: vec![],
            kwargs: vec![],
            inline_text: None,
            content: vec![],
        };
        let element = JsonlElement::Block(block);
        let mut line_index = 1;
        let result = emitter.process_element(element, &mut line_index, 1);
        assert!(result.is_ok());
    }

    #[test]
    fn test_process_element_block_with_nested_content() {
        let emitter = JsonlEmitter::from_stdin();
        let nested_text = JsonlElement::Text {
            lines: vec!["Nested text".to_string()],
        };
        let block = JsonlBlock {
            block_type: "section".to_string(),
            id: Some("main".to_string()),
            level: 1,
            is_void: false,
            is_context_line: false,
            args: vec![],
            kwargs: vec![],
            inline_text: None,
            content: vec![nested_text],
        };
        let element = JsonlElement::Block(block);
        let mut line_index = 1;
        let result = emitter.process_element(element, &mut line_index, 1);
        assert!(result.is_ok());
    }

    #[test]
    fn test_jsonl_block_serialization() {
        let block = JsonlBlock {
            block_type: "paragraph".to_string(),
            id: Some("test".to_string()),
            level: 1,
            is_void: false,
            is_context_line: false,
            args: vec!["arg1".into()],
            kwargs: vec![("key".into(), "value".into())],
            inline_text: Some("text".into()),
            content: vec![],
        };
        let json = serde_json::to_string(&block).unwrap();
        assert!(json.contains("paragraph"));
        assert!(json.contains("test"));
        assert!(json.contains("arg1"));
    }

    #[test]
    fn test_jsonl_element_text_serialization() {
        let element = JsonlElement::Text {
            lines: vec!["line1".to_string(), "line2".to_string()],
        };
        let json = serde_json::to_string(&element).unwrap();
        assert!(json.contains("text"));
        assert!(json.contains("line1"));
        assert!(json.contains("line2"));
    }

    #[test]
    fn test_jsonl_element_block_serialization() {
        let block = JsonlBlock {
            block_type: "div".to_string(),
            id: None,
            level: 1,
            is_void: false,
            is_context_line: false,
            args: vec![],
            kwargs: vec![],
            inline_text: None,
            content: vec![],
        };
        let element = JsonlElement::Block(block);
        let json = serde_json::to_string(&element).unwrap();
        assert!(json.contains("block"));
        assert!(json.contains("div"));
    }

    #[test]
    fn test_emitter_subscribe() {
        let emitter = JsonlEmitter::from_stdin();
        let _reader = emitter.subscribe();
        // Test that we can create a subscriber without errors
    }

    #[test]
    fn test_multiple_jsonl_lines() {
        let emitter = JsonlEmitter::from_stdin();
        let jsonl_content = concat!(
            r#"{"type":"text","lines":["First line"]}"#,
            "\n",
            r#"{"type":"block","block_type":"div","id":null,"args":[],"kwargs":[],"inline_text":null,"content":[]}"#,
            "\n",
            r#"{"type":"text","lines":["Last line"]}"#
        );
        let reader = Box::new(Cursor::new(jsonl_content.as_bytes()));
        let result = emitter.process_jsonl(reader);
        assert!(result.is_ok());
    }

    #[test]
    fn test_jsonl_with_special_characters() {
        let emitter = JsonlEmitter::from_stdin();
        let jsonl_content =
            r#"{"type":"text","lines":["Hello \"world\"","Line with\ttab","Line with\nnewline"]}"#;
        let reader = Box::new(Cursor::new(jsonl_content.as_bytes()));
        let result = emitter.process_jsonl(reader);
        assert!(result.is_ok());
    }

    #[test]
    fn test_jsonl_block_deserialization() {
        let json = r#"{"block_type":"test","id":"123","args":["a"],"kwargs":[["k","v"]],"inline_text":"text","content":[]}"#;
        let block: JsonlBlock = serde_json::from_str(json).unwrap();
        assert_eq!(block.block_type, "test");
        assert_eq!(block.id, Some("123".to_string()));
        assert_eq!(block.args.len(), 1);
        assert_eq!(block.args[0].content, "a");
        assert_eq!(block.kwargs.len(), 1);
        assert_eq!(block.kwargs[0].0.content, "k");
        assert_eq!(block.kwargs[0].1.content, "v");
        assert!(block.inline_text.is_some());
        assert_eq!(block.inline_text.unwrap().content, "text");
    }

    #[test]
    fn test_skip_empty_lines() {
        let jsonl_content = r#"

{"type": "text", "lines": ["Test line"]}

"#;

        let emitter = create_emitter_from_string(jsonl_content);
        let receiver = emitter.subscribe();

        let content_clone = jsonl_content.to_string();
        let handle = thread::spawn(move || {
            emitter.run_with_content(content_clone);
        });

        let events: Vec<Event> = receiver.collect();
        handle.join().unwrap();

        assert_eq!(events.len(), 1);
        match &events[0] {
            Event::TextFinished { lines, .. } => {
                assert_eq!(lines[0], "Test line");
            }
            _ => panic!("Expected TextFinished event"),
        }
    }

    #[test]
    fn test_block_with_all_optional_fields_none() {
        let jsonl_content = r#"{"type": "block", "block_type": "simple", "id": null, "args": [], "kwargs": [], "inline_text": null, "content": []}"#;

        let emitter = create_emitter_from_string(jsonl_content);
        let receiver = emitter.subscribe();

        let content_clone = jsonl_content.to_string();
        let handle = thread::spawn(move || {
            emitter.run_with_content(content_clone);
        });

        let events: Vec<Event> = receiver.collect();
        handle.join().unwrap();

        assert_eq!(events.len(), 2);
        match &events[0] {
            Event::BlockStarted { meta_line } => {
                assert_eq!(meta_line.type_name, "simple");
                assert_eq!(meta_line.id.value, "");
                assert!(meta_line.arguments.args.is_empty());
                assert!(meta_line.arguments.kwargs.is_empty());
                assert_eq!(meta_line.inline_text.content, "");
            }
            _ => panic!("Expected BlockStarted event"),
        }
    }

    #[test]
    fn test_malformed_json_error() {
        let jsonl_content = r#"{"type": "block", "block_type": "invalid"#; // Invalid JSON

        let emitter = JsonlEmitter {
            bus: Arc::new(Mutex::new(Bus::new(1000))),
            file_path: None,
            use_stdin: false,
        };

        let reader = Box::new(Cursor::new(jsonl_content.as_bytes()));
        let result = emitter.process_jsonl(reader);

        assert!(result.is_err());
        assert!(result.unwrap_err().kind() == io::ErrorKind::InvalidData);
    }

    fn create_emitter_from_string(_content: &str) -> JsonlEmitter {
        JsonlEmitter {
            bus: Arc::new(Mutex::new(Bus::new(1000))),
            file_path: None,
            use_stdin: false,
        }
    }

    // Helper method to create emitter with custom processing
    impl JsonlEmitter {
        #[cfg(test)]
        fn run_with_content(self, content: String) {
            let reader = Box::new(Cursor::new(content.into_bytes()));
            if let Err(e) = self.process_jsonl(reader) {
                eprintln!("Error processing JSONL: {e}");
            }
        }
    }
}
