use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;

fn main() {
    // from file
    let lines = read_lines("examples/demo.wasot").expect("Failed to read lines from file");
    for event in parse_wasot(lines) {
        println!("{:?}", event.expect("Failed to parse event"));
    }

    let lines_vec: Vec<String> = vec![
        "START".to_string(),
        "MSG: Data is from a vector.".to_string(),
        "STOP: In-memory processing complete.".to_string(),
    ];
    for event in parse_wasot(lines_vec.into_iter().map(Ok)) {
        println!("{:?}", event.expect("Failed to parse event"));
    }

    let lines = read_lines("examples/demo.wasot").expect("Failed to read lines from file");
    let events = parse_wasot(lines);

    for line in to_wasot(events) {
        println!("{line}");
    }
}

/// Opens a file and returns a lazy line-by-line iterator.
/// The file is not read into memory all at once. Instead, each line is read
/// on demand as the iterator is consumed.
fn read_lines(path: impl AsRef<Path>) -> io::Result<impl Iterator<Item = io::Result<String>>> {
    // Attempt to open the file. The '?' operator will return an error if it fails.
    let file = File::open(path)?;
    // Wrap the file in a BufReader and return an iterator over its lines.
    let reader = io::BufReader::new(file);
    Ok(reader.lines())
}

fn parse_wasot(
    lines: impl Iterator<Item = io::Result<String>>,
) -> impl Iterator<Item = Result<Event, io::Error>> {
    lines.map(|line_result| {
        let line = line_result?;
        Ok(Event { line })
    })
}

fn to_wasot(
    events: impl Iterator<Item = Result<Event, io::Error>>,
) -> impl Iterator<Item = String> {
    events.map(|event_result| {
        let event = event_result.expect("Failed to get event");
        event.line
    })
}

#[derive(Debug)]
pub struct Event {
    pub line: String,
}

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

    #[test]
    fn test_read_lines_success() {
        // Create a temporary file with test content
        let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
        writeln!(temp_file, "line1").unwrap();
        writeln!(temp_file, "line2").unwrap();
        writeln!(temp_file, "line3").unwrap();

        // Test line 32 (File::open) and lines 36-37 (BufReader creation and lines())
        let lines = read_lines(temp_file.path()).expect("Should read lines successfully");
        let collected: Result<Vec<String>, _> = lines.collect();

        assert!(collected.is_ok());
        let lines_vec = collected.unwrap();
        assert_eq!(lines_vec.len(), 3);
        assert_eq!(lines_vec[0], "line1");
        assert_eq!(lines_vec[1], "line2");
        assert_eq!(lines_vec[2], "line3");
    }

    #[test]
    fn test_read_lines_file_not_found() {
        // Test line 34 (File::open error path)
        let result = read_lines("/nonexistent/file/path");
        assert!(result.is_err());

        // Verify it's an IO error
        match result {
            Err(e) => assert_eq!(e.kind(), std::io::ErrorKind::NotFound),
            Ok(_) => panic!("Expected an error for non-existent file"),
        }
    }

    #[test]
    fn test_parse_wasot_success() {
        // Test lines 43-45 in parse_wasot function
        let input_lines = vec![
            Ok("first line".to_string()),
            Ok("second line".to_string()),
            Ok("third line".to_string()),
        ];

        let events: Result<Vec<_>, _> = parse_wasot(input_lines.into_iter()).collect();
        assert!(events.is_ok());

        let events = events.unwrap();
        assert_eq!(events.len(), 3);
        assert_eq!(events[0].line, "first line");
        assert_eq!(events[1].line, "second line");
        assert_eq!(events[2].line, "third line");
    }

    #[test]
    fn test_parse_wasot_with_io_error() {
        // Test lines 43-45 with IO error propagation
        let input_lines = vec![
            Ok("good line".to_string()),
            Err(std::io::Error::new(
                std::io::ErrorKind::BrokenPipe,
                "Test error",
            )),
            Ok("another good line".to_string()),
        ];

        let mut events = parse_wasot(input_lines.into_iter());

        // First event should succeed
        let first = events.next().unwrap();
        assert!(first.is_ok());
        assert_eq!(first.unwrap().line, "good line");

        // Second should be an error
        let second = events.next().unwrap();
        assert!(second.is_err());

        // Third should succeed
        let third = events.next().unwrap();
        assert!(third.is_ok());
        assert_eq!(third.unwrap().line, "another good line");
    }

    #[test]
    fn test_to_wasot_success() {
        // Test lines 52-54 in to_wasot function
        let events = vec![
            Ok(Event {
                line: "event1".to_string(),
            }),
            Ok(Event {
                line: "event2".to_string(),
            }),
            Ok(Event {
                line: "event3".to_string(),
            }),
        ];

        let lines: Vec<String> = to_wasot(events.into_iter()).collect();
        assert_eq!(lines.len(), 3);
        assert_eq!(lines[0], "event1");
        assert_eq!(lines[1], "event2");
        assert_eq!(lines[2], "event3");
    }

    #[test]
    #[should_panic(expected = "Failed to get event")]
    fn test_to_wasot_with_error() {
        // Test lines 52-54 with error case (expect panic)
        let events = vec![
            Ok(Event {
                line: "good event".to_string(),
            }),
            Err(std::io::Error::new(
                std::io::ErrorKind::BrokenPipe,
                "Test error",
            )),
        ];

        // This should panic on the error case due to expect() on line 53
        let _: Vec<String> = to_wasot(events.into_iter()).collect();
    }

    #[test]
    fn test_event_debug_format() {
        let event = Event {
            line: "test line".to_string(),
        };
        let debug_str = format!("{event:?}");
        assert!(debug_str.contains("Event"));
        assert!(debug_str.contains("test line"));
    }

    #[test]
    fn test_empty_input() {
        // Test empty input for parse_wasot
        let empty_lines: Vec<Result<String, std::io::Error>> = vec![];
        let events: Vec<_> = parse_wasot(empty_lines.into_iter()).collect();
        assert!(events.is_empty());

        // Test empty input for to_wasot
        let empty_events: Vec<Result<Event, std::io::Error>> = vec![];
        let lines: Vec<String> = to_wasot(empty_events.into_iter()).collect();
        assert!(lines.is_empty());
    }

    #[test]
    fn test_single_line_input() {
        // Test single line input
        let input_lines = vec![Ok("single line".to_string())];
        let events: Result<Vec<_>, _> = parse_wasot(input_lines.into_iter()).collect();
        assert!(events.is_ok());
        let events = events.unwrap();
        assert_eq!(events.len(), 1);
        assert_eq!(events[0].line, "single line");
    }

    #[test]
    fn test_lines_with_special_characters() {
        // Test lines with various special characters and encodings
        let special_lines = vec![
            Ok("line with unicode: 🚀🎯".to_string()),
            Ok("line with tabs:\tand\tnewlines\n".to_string()),
            Ok("line with quotes: \"hello\" and 'world'".to_string()),
            Ok("empty line follows".to_string()),
            Ok("".to_string()),
        ];

        let events: Result<Vec<_>, _> = parse_wasot(special_lines.into_iter()).collect();
        assert!(events.is_ok());
        let events = events.unwrap();
        assert_eq!(events.len(), 5);
        assert_eq!(events[0].line, "line with unicode: 🚀🎯");
        assert_eq!(events[1].line, "line with tabs:\tand\tnewlines\n");
        assert_eq!(events[2].line, "line with quotes: \"hello\" and 'world'");
        assert_eq!(events[3].line, "empty line follows");
        assert_eq!(events[4].line, "");
    }

    #[test]
    fn test_very_long_line() {
        // Test handling of very long lines
        let long_line = "x".repeat(10000);
        let input_lines = vec![Ok(long_line.clone())];

        let events: Result<Vec<_>, _> = parse_wasot(input_lines.into_iter()).collect();
        assert!(events.is_ok());
        let events = events.unwrap();
        assert_eq!(events.len(), 1);
        assert_eq!(events[0].line.len(), 10000);
        assert_eq!(events[0].line, long_line);
    }

    #[test]
    fn test_multiple_consecutive_errors() {
        // Test handling of multiple consecutive IO errors
        let input_lines = vec![
            Ok("good line 1".to_string()),
            Err(std::io::Error::new(
                std::io::ErrorKind::PermissionDenied,
                "Error 1",
            )),
            Err(std::io::Error::new(
                std::io::ErrorKind::UnexpectedEof,
                "Error 2",
            )),
            Ok("good line 2".to_string()),
        ];

        let mut events = parse_wasot(input_lines.into_iter());

        // First should succeed
        let first = events.next().unwrap();
        assert!(first.is_ok());
        assert_eq!(first.unwrap().line, "good line 1");

        // Second should be error
        let second = events.next().unwrap();
        assert!(second.is_err());

        // Third should also be error
        let third = events.next().unwrap();
        assert!(third.is_err());

        // Fourth should succeed
        let fourth = events.next().unwrap();
        assert!(fourth.is_ok());
        assert_eq!(fourth.unwrap().line, "good line 2");
    }

    #[test]
    fn test_read_lines_with_permissions() {
        // Test attempting to read a directory instead of a file
        let result = read_lines("/");
        // This should either succeed (if permissions allow) or fail with an appropriate error
        match result {
            Ok(_) => {
                // If it succeeds, it's likely because the OS allows reading directories
                // as text in some contexts
            }
            Err(e) => {
                // More likely - should get a permission denied or "is a directory" error
                assert!(matches!(
                    e.kind(),
                    std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::IsADirectory
                ));
            }
        }
    }

    #[test]
    fn test_event_equality() {
        // Test Event struct behavior
        let event1 = Event {
            line: "test line".to_string(),
        };
        let event2 = Event {
            line: "test line".to_string(),
        };
        let event3 = Event {
            line: "different line".to_string(),
        };

        // Test debug formatting
        let debug1 = format!("{event1:?}");
        let debug2 = format!("{event2:?}");
        assert_eq!(debug1, debug2);

        // Test that different events have different debug representations
        let debug3 = format!("{event3:?}");
        assert_ne!(debug1, debug3);
    }

    #[test]
    fn test_to_wasot_with_empty_lines() {
        // Test to_wasot with events containing empty lines
        let events = vec![
            Ok(Event {
                line: "first".to_string(),
            }),
            Ok(Event {
                line: "".to_string(),
            }),
            Ok(Event {
                line: "third".to_string(),
            }),
        ];

        let lines: Vec<String> = to_wasot(events.into_iter()).collect();
        assert_eq!(lines.len(), 3);
        assert_eq!(lines[0], "first");
        assert_eq!(lines[1], "");
        assert_eq!(lines[2], "third");
    }

    #[test]
    fn test_parse_wasot_preserves_whitespace() {
        // Test that parse_wasot preserves all whitespace exactly
        let input_lines = vec![
            Ok("  leading spaces".to_string()),
            Ok("trailing spaces  ".to_string()),
            Ok("\ttab at start".to_string()),
            Ok("mixed\t\tspaces and\ttabs".to_string()),
        ];

        let events: Result<Vec<_>, _> = parse_wasot(input_lines.into_iter()).collect();
        assert!(events.is_ok());
        let events = events.unwrap();
        assert_eq!(events.len(), 4);
        assert_eq!(events[0].line, "  leading spaces");
        assert_eq!(events[1].line, "trailing spaces  ");
        assert_eq!(events[2].line, "\ttab at start");
        assert_eq!(events[3].line, "mixed\t\tspaces and\ttabs");
    }

    #[test]
    fn test_chain_parse_and_to_wasot() {
        // Test the round-trip: lines -> parse_wasot -> to_wasot -> lines
        let original_lines = [
            "first line".to_string(),
            "second line with 🎯".to_string(),
            "".to_string(),
            "fourth line".to_string(),
        ];

        // Convert to Result<String, io::Error> for parse_wasot
        let input_lines = original_lines.iter().cloned().map(Ok).collect::<Vec<_>>();

        // Parse and then convert back
        let events = parse_wasot(input_lines.into_iter());
        let result_lines: Vec<String> = to_wasot(events).collect();

        // Should be identical
        assert_eq!(result_lines.len(), original_lines.len());
        for (original, result) in original_lines.iter().zip(result_lines.iter()) {
            assert_eq!(original, result);
        }
    }
}
