//! DO NOT DELETE ANY TESTS OR WEAKEN THEM IN ANY WAY

use super::*;
use crate::events::EventKind;
use crate::{ParseErrorKind, ScalarKind};
use facet_testhelpers::test;
use styx_testhelpers::{ActualError, assert_annotated_errors, source_without_annotations};
use tracing::trace;

mod event_assert {
    use super::*;
    use ariadne::{Color, Label, Report, ReportKind, Source};
    use similar::{ChangeTag, TextDiff};
    use std::fmt::Write;

    /// Format an event to a string representation, ignoring spans.
    fn format_event(event: &Event<'_>) -> String {
        match &event.kind {
            EventKind::DocumentStart => "DocumentStart".to_string(),
            EventKind::DocumentEnd => "DocumentEnd".to_string(),
            EventKind::ObjectStart => "ObjectStart".to_string(),
            EventKind::ObjectEnd => "ObjectEnd".to_string(),
            EventKind::SequenceStart => "SequenceStart".to_string(),
            EventKind::SequenceEnd => "SequenceEnd".to_string(),
            EventKind::EntryStart => "EntryStart".to_string(),
            EventKind::EntryEnd => "EntryEnd".to_string(),
            EventKind::Key { tag, payload, kind } => {
                let mut s = String::from("Key(");
                if let Some(t) = tag {
                    write!(s, "@{} ", t).unwrap();
                }
                match payload {
                    Some(p) => write!(s, "{:?}", p.as_ref()).unwrap(),
                    None => s.push_str("unit"),
                }
                if *kind != ScalarKind::Bare {
                    write!(s, ", {:?}", kind).unwrap();
                }
                s.push(')');
                s
            }
            EventKind::Scalar { value, kind } => {
                let mut s = String::from("Scalar(");
                write!(s, "{:?}", value.as_ref()).unwrap();
                if *kind != ScalarKind::Bare {
                    write!(s, ", {:?}", kind).unwrap();
                }
                s.push(')');
                s
            }
            EventKind::Unit => "Unit".to_string(),
            EventKind::TagStart { name } => format!("TagStart(@{})", name),
            EventKind::TagEnd => "TagEnd".to_string(),
            EventKind::Comment { text } => format!("Comment({:?})", text),
            EventKind::DocComment { lines } => format!("DocComment({:?})", lines),
            EventKind::Error { kind } => format!("Error({:?})", kind),
        }
    }

    /// Format a list of events to a multi-line string.
    fn format_events(events: &[Event<'_>]) -> String {
        events
            .iter()
            .map(format_event)
            .collect::<Vec<_>>()
            .join("\n")
    }

    /// Normalize expected string: trim, dedent, remove empty lines at start/end.
    fn normalize_expected(expected: &str) -> String {
        let lines: Vec<&str> = expected.lines().collect();

        // Find minimum indentation (ignoring empty lines)
        let min_indent = lines
            .iter()
            .filter(|l| !l.trim().is_empty())
            .map(|l| l.len() - l.trim_start().len())
            .min()
            .unwrap_or(0);

        // Dedent and filter empty lines at start/end
        let dedented: Vec<&str> = lines
            .iter()
            .map(|l| {
                if l.len() >= min_indent {
                    &l[min_indent..]
                } else {
                    l.trim()
                }
            })
            .collect();

        // Trim empty lines from start and end
        let start = dedented.iter().position(|l| !l.is_empty()).unwrap_or(0);
        let end = dedented
            .iter()
            .rposition(|l| !l.is_empty())
            .map(|i| i + 1)
            .unwrap_or(0);

        dedented[start..end].join("\n")
    }

    /// Print each event with its span annotated on the source using ariadne.
    fn print_events_with_spans(source: &str, events: &[Event<'_>]) {
        eprintln!("\n=== Events with spans ===\n");

        for (i, event) in events.iter().enumerate() {
            let label = format_event(event);
            let (start, end) = event_span(event);

            eprintln!("Event {}: {}", i + 1, label);

            let mut buf = Vec::new();
            Report::build(ReportKind::Custom("", Color::Cyan), ("", start..end))
                .with_label(
                    Label::new(("", start..end))
                        .with_message(&label)
                        .with_color(Color::Cyan),
                )
                .finish()
                .write(("", Source::from(source)), &mut buf)
                .unwrap();
            eprintln!("{}", String::from_utf8_lossy(&buf));
        }
    }

    /// Extract span from an event.
    fn event_span(event: &Event<'_>) -> (usize, usize) {
        (event.span.start as usize, event.span.end as usize)
    }

    /// Assert that events match the expected string representation.
    /// On mismatch, shows expected, actual, and a diff.
    /// Set STYX_SHOW_SPANS=1 to see each event with its span annotated on the source.
    pub fn assert_events_eq_impl(source: &str, events: &[Event<'_>], expected: &str) {
        let actual = format_events(events);
        let expected = normalize_expected(expected);

        if actual == expected {
            return;
        }

        eprintln!("\n╭─ Events mismatch! ─────────────────────────────────────────╮\n");

        eprintln!("Expected:\n{}\n", indent(&expected, "    "));
        eprintln!("Actual:\n{}\n", indent(&actual, "    "));

        eprintln!("Diff:");
        let diff = TextDiff::from_lines(&expected, &actual);
        for change in diff.iter_all_changes() {
            let (sign, color) = match change.tag() {
                ChangeTag::Delete => ("-", "\x1b[31m"),
                ChangeTag::Insert => ("+", "\x1b[32m"),
                ChangeTag::Equal => (" ", ""),
            };
            let reset = if color.is_empty() { "" } else { "\x1b[0m" };
            eprint!("  {}{}{}{}", color, sign, change, reset);
        }
        eprintln!();

        if std::env::var("STYX_SHOW_SPANS").is_ok() {
            print_events_with_spans(source, events);
        } else {
            eprintln!("Hint: rerun with STYX_SHOW_SPANS=1 to see spans annotated on source\n");
        }

        eprintln!("╰────────────────────────────────────────────────────────────╯\n");

        panic!("Events do not match expected");
    }

    fn indent(s: &str, prefix: &str) -> String {
        s.lines()
            .map(|l| format!("{}{}", prefix, l))
            .collect::<Vec<_>>()
            .join("\n")
    }
}

macro_rules! assert_events_eq {
    ($source:expr, $events:expr, $expected:expr) => {
        event_assert::assert_events_eq_impl($source, &$events, $expected)
    };
}

fn parse(source: &str) -> Vec<Event<'_>> {
    Parser::new(source).parse_to_vec()
}

fn error_kind_name(kind: &ParseErrorKind) -> &'static str {
    match kind {
        ParseErrorKind::UnexpectedToken => "UnexpectedToken",
        ParseErrorKind::UnclosedObject => "UnclosedObject",
        ParseErrorKind::UnclosedSequence => "UnclosedSequence",
        ParseErrorKind::InvalidEscape(_) => "InvalidEscape",
        ParseErrorKind::ExpectedKey => "ExpectedKey",
        ParseErrorKind::ExpectedValue => "ExpectedValue",
        ParseErrorKind::UnexpectedEof => "UnexpectedEof",
        ParseErrorKind::DuplicateKey { .. } => "DuplicateKey",
        ParseErrorKind::InvalidTagName => "InvalidTagName",
        ParseErrorKind::InvalidKey => "InvalidKey",
        ParseErrorKind::DanglingDocComment => "DanglingDocComment",
        ParseErrorKind::TooManyAtoms => "TooManyAtoms",
        ParseErrorKind::ReopenedPath { .. } => "ReopenedPath",
        ParseErrorKind::NestIntoTerminal { .. } => "NestIntoTerminal",
        ParseErrorKind::CommaInSequence => "CommaInSequence",
        ParseErrorKind::MissingWhitespaceBeforeBlock => "MissingWhitespaceBeforeBlock",
        ParseErrorKind::TrailingContent => "TrailingContent",
    }
}

fn assert_parse_errors(annotated_source: &str) {
    let source = source_without_annotations(annotated_source);
    let events = parse(&source);
    let actual_errors: Vec<_> = events
        .iter()
        .filter_map(|e| match e {
            Event {
                kind: EventKind::Error { kind },
                span,
            } => Some(ActualError {
                span: (*span).into(),
                kind: error_kind_name(kind).to_string(),
            }),
            _ => None,
        })
        .collect();
    assert_annotated_errors(annotated_source, actual_errors);
}

#[test]
fn test_empty_document() {
    let events = parse("");
    assert!(
        events
            .iter()
            .any(|e| matches!(e.kind, EventKind::DocumentStart))
    );
    assert!(
        events
            .iter()
            .any(|e| matches!(e.kind, EventKind::DocumentEnd))
    );
}

#[test]
fn test_simple_entry() {
    let events = parse("foo bar");
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::Key { payload: Some(v), .. } if v == "foo"))
    );
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::Scalar { value, .. } if value == "bar"))
    );
}

#[test]
fn test_key_only() {
    let events = parse("foo");
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::Key { payload: Some(v), .. } if v == "foo"))
    );
    assert!(events.iter().any(|e| matches!(e.kind, EventKind::Unit)));
}

#[test]
fn test_multiple_entries() {
    let events = parse("foo bar\nbaz qux");
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(v), ..
            } => Some(v.as_ref()),
            _ => None,
        })
        .collect();
    assert_eq!(keys, vec!["foo", "baz"]);
}

#[test]
fn test_quoted_string() {
    let events = parse(r#"name "hello world""#);
    assert!(events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::Scalar { value, kind: ScalarKind::Quoted, .. } if value == "hello world")));
}

#[test]
fn test_quoted_escape() {
    let events = parse(r#"msg "hello\nworld""#);
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::Scalar { value, .. } if value == "hello\nworld"))
    );
}

#[test]
fn test_too_many_atoms() {
    let events = parse("a b c");
    eprintln!("EVENTS:");
    for (i, e) in events.iter().enumerate() {
        eprintln!("  {i}: {e:?}");
    }
    assert_parse_errors(
        r#"
a b c
    ^ TooManyAtoms
"#,
    );
}

#[test]
fn test_too_many_atoms_in_object() {
    assert_parse_errors(
        r#"
{label ": BIGINT" line 4}
                  ^^^^ TooManyAtoms
"#,
    );
}

#[test]
fn test_unit_value() {
    let events = parse("flag @");
    for _e in &events {
        trace!(?_e, "event");
    }
    assert!(events.iter().any(|e| matches!(e.kind, EventKind::Unit)));
}

#[test]
fn test_unit_key() {
    let events = parse("@ value");
    assert!(events.iter().any(|e| matches!(
        &e.kind,
        EventKind::Key {
            payload: None,
            tag: None,
            ..
        }
    )));
}

#[test]
fn test_tag() {
    let events = parse("type @user");
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::TagStart { name, .. } if *name == "user"))
    );
}

#[test]
fn test_comments() {
    let events = parse("// comment\nfoo bar");
    assert!(
        events
            .iter()
            .any(|e| matches!(e.kind, EventKind::Comment { .. }))
    );
}

#[test]
fn test_doc_comments() {
    let events = parse("/// doc\nfoo bar");
    assert!(
        events
            .iter()
            .any(|e| matches!(e.kind, EventKind::DocComment { .. }))
    );
}

#[test]
fn test_doc_comment_at_eof_error() {
    assert_parse_errors(
        r#"
foo bar
/// dangling
^^^^^^^^^^^^ DanglingDocComment
"#,
    );
}

#[test]
fn test_nested_object() {
    let events = parse("outer {inner {x 1}}");
    let obj_starts = events
        .iter()
        .filter(|e| matches!(e.kind, EventKind::ObjectStart))
        .count();
    assert!(obj_starts >= 2);
}

#[test]
fn test_sequence_elements() {
    let events = parse("items (a b c)");
    let scalars: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Scalar { value, .. } => Some(value.as_ref()),
            _ => None,
        })
        .collect();
    assert!(scalars.contains(&"a"));
    assert!(scalars.contains(&"b"));
    assert!(scalars.contains(&"c"));
}

#[test]
fn test_tagged_object() {
    let events = parse("result @err{message oops}");
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::TagStart { name, .. } if *name == "err"))
    );
}

#[test]
fn test_tagged_explicit_unit() {
    let events = parse("nothing @empty@");
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::TagStart { name, .. } if *name == "empty"))
    );
}

#[test]
fn test_simple_attribute() {
    let events = parse("server host>localhost");
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(v), ..
            } => Some(v.as_ref()),
            _ => None,
        })
        .collect();
    assert!(keys.contains(&"server"));
    assert!(keys.contains(&"host"));
}

#[test]
fn test_multiple_attributes() {
    let events = parse("server host>localhost port>8080");
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(v), ..
            } => Some(v.as_ref()),
            _ => None,
        })
        .collect();
    assert!(keys.contains(&"server"));
    assert!(keys.contains(&"host"));
    assert!(keys.contains(&"port"));
}

#[test]
fn test_attribute_with_object_value() {
    let events = parse("config opts>{x 1}");
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(v), ..
            } => Some(v.as_ref()),
            _ => None,
        })
        .collect();
    assert!(keys.contains(&"config"));
    assert!(keys.contains(&"opts"));
    assert!(keys.contains(&"x"));
}

#[test]
fn test_attribute_with_sequence_value() {
    let events = parse("config tags>(a b c)");
    assert!(
        events
            .iter()
            .any(|e| matches!(e.kind, EventKind::SequenceStart))
    );
}

#[test]
fn test_attribute_with_tag_value() {
    let events = parse("config status>@ok");
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::TagStart { name, .. } if *name == "ok"))
    );
}

#[test]
fn test_tag_with_dot_invalid() {
    assert_parse_errors(
        r#"
@Some.Type
^^^^^^^^^^ InvalidTagName
"#,
    );
}

#[test]
fn test_invalid_tag_name_starts_with_digit() {
    assert_parse_errors(
        r#"
x @123
  ^^^^ InvalidTagName
"#,
    );
}

#[test]
fn test_unicode_escape_braces() {
    let events = parse(r#"x "\u{1F600}""#);
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::Scalar { value, .. } if value == "😀"))
    );
}

#[test]
fn test_unicode_escape_4digit() {
    let events = parse(r#"x "\u0041""#);
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::Scalar { value, .. } if value == "A"))
    );
}

#[test]
fn test_heredoc_key_rejected() {
    assert_parse_errors(
        r#"
<<EOF
^^^^^^ InvalidKey
key
EOF
 value
"#,
    );
}

#[test]
fn test_missing_comma_rejected() {
    assert_parse_errors(
        r#"
{server {host localhost port 8080}}
                        ^^^^ TooManyAtoms
"#,
    );
}

#[test]
fn test_bare_scalar_is_string() {
    let events = parse("port 8080");
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::Scalar { value, .. } if value == "8080"))
    );
}

#[test]
fn test_bool_like_is_string() {
    let events = parse("enabled true");
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::Scalar { value, .. } if value == "true"))
    );
}

// Additional tests from old Parser

#[test]
fn test_doc_comment_followed_by_entry_ok() {
    assert_parse_errors("/// documentation\nkey value");
}

#[test]
fn test_doc_comment_before_closing_brace_error() {
    assert_parse_errors(
        r#"
{foo bar
/// dangling
^^^^^^^^^^^^ DanglingDocComment
}
"#,
    );
}

#[test]
fn test_multiple_doc_comments_before_entry_ok() {
    assert_parse_errors("/// line 1\n/// line 2\nkey value");
}

#[test]
fn test_multiline_doc_comment_in_object() {
    // Multiple consecutive doc comments inside a braced object should be joined into one event
    let source = "schema {\n    /// First line\n    /// Second line\n    /// Third line\n    field @string\n}";
    let events = parse(source);
    let doc_comments: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::DocComment { lines, .. } => Some(lines.clone()),
            _ => None,
        })
        .collect();
    // Should be one event with all lines, without the `/// ` prefix
    assert_eq!(
        doc_comments,
        vec![vec!["First line", "Second line", "Third line"]],
        "consecutive doc comments should be joined into one event"
    );
}

#[test]
fn test_object_with_entries() {
    let events = parse("config {host localhost, port 8080}");
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(value),
                ..
            } => Some(value.as_ref()),
            _ => None,
        })
        .collect();
    assert!(keys.contains(&"config"));
    assert!(keys.contains(&"host"));
    assert!(keys.contains(&"port"));
}

#[test]
fn test_nested_sequences() {
    let events = parse("matrix ((1 2) (3 4))");
    let seq_starts = events
        .iter()
        .filter(|e| matches!(e.kind, EventKind::SequenceStart))
        .count();
    assert_eq!(seq_starts, 3);
}

#[test]
fn test_tagged_sequence() {
    let events = parse("color @rgb(255 128 0)");
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::TagStart { name, .. } if *name == "rgb"))
    );
    assert!(
        events
            .iter()
            .any(|e| matches!(e.kind, EventKind::SequenceStart))
    );
}

#[test]
fn test_tagged_scalar() {
    let events = parse(r#"name @nickname"Bob""#);
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::TagStart { name, .. } if *name == "nickname"))
    );
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::Scalar { value, .. } if value == "Bob"))
    );
}

#[test]
fn test_chained_tag_events() {
    let source = "events (@must_emit/@discover_start{executor default} @must_not_emit/@exec_start)";
    let events = parse(source);
    assert_events_eq!(
        source,
        events,
        r#"
        DocumentStart
        ObjectStart
        EntryStart
        Key("events")
        SequenceStart
        TagStart(@must_emit)
        TagStart(@discover_start)
        ObjectStart
        EntryStart
        Key("executor")
        Scalar("default")
        EntryEnd
        ObjectEnd
        TagEnd
        TagEnd
        TagStart(@must_not_emit)
        TagStart(@exec_start)
        TagEnd
        TagEnd
        SequenceEnd
        EntryEnd
        ObjectEnd
        DocumentEnd
        "#
    );
}

#[test]
fn test_chained_tag_is_not_sequence_payload() {
    let events = parse("x @must_emit/@discover_end");
    let sequence_starts = events
        .iter()
        .filter(|e| matches!(e.kind, EventKind::SequenceStart))
        .count();
    assert_eq!(sequence_starts, 0);
    let tags: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::TagStart { name } => Some(*name),
            _ => None,
        })
        .collect();
    assert_eq!(tags, vec!["must_emit", "discover_end"]);
}

#[test]
fn test_three_segment_chained_tag_events() {
    let source = "x @a/@b/@c";
    let events = parse(source);
    assert_events_eq!(
        source,
        events,
        r#"
        DocumentStart
        ObjectStart
        EntryStart
        Key("x")
        TagStart(@a)
        TagStart(@b)
        TagStart(@c)
        TagEnd
        TagEnd
        TagEnd
        EntryEnd
        ObjectEnd
        DocumentEnd
        "#
    );
}

#[test]
fn test_chained_tag_with_quoted_leaf_payload_events() {
    let source = r#"x @a/@b"foo""#;
    let events = parse(source);
    assert_events_eq!(
        source,
        events,
        r#"
        DocumentStart
        ObjectStart
        EntryStart
        Key("x")
        TagStart(@a)
        TagStart(@b)
        Scalar("foo", Quoted)
        TagEnd
        TagEnd
        EntryEnd
        ObjectEnd
        DocumentEnd
        "#
    );
}

#[test]
fn test_chained_tag_with_raw_leaf_payload_events() {
    let source = r##"x @a/@br#"foo"#"##;
    let events = parse(source);
    assert_events_eq!(
        source,
        events,
        r#"
        DocumentStart
        ObjectStart
        EntryStart
        Key("x")
        TagStart(@a)
        TagStart(@b)
        Scalar("foo", Raw)
        TagEnd
        TagEnd
        EntryEnd
        ObjectEnd
        DocumentEnd
        "#
    );
}

#[test]
fn test_chained_tag_with_heredoc_leaf_payload_events() {
    let source = "x @a/@b<<EOF\nhello\nEOF";
    let events = parse(source);
    assert_events_eq!(
        source,
        events,
        r#"
        DocumentStart
        ObjectStart
        EntryStart
        Key("x")
        TagStart(@a)
        TagStart(@b)
        Scalar("hello\n", Heredoc)
        TagEnd
        TagEnd
        EntryEnd
        ObjectEnd
        DocumentEnd
        "#
    );
}

#[test]
fn test_invalid_chained_tag_name_segment() {
    assert_parse_errors(
        r#"
x @a/@1
  ^^^^^ InvalidTagName
"#,
    );
}

#[test]
fn test_tag_whitespace_gap() {
    let events = parse("x @tag\ny {a b}");
    let tag_events: Vec<_> = events
        .iter()
        .filter(|e| matches!(e.kind, EventKind::TagStart { .. } | EventKind::TagEnd))
        .collect();
    assert_eq!(tag_events.len(), 2);
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(value),
                ..
            } => Some(value.as_ref()),
            _ => None,
        })
        .collect();
    assert!(keys.contains(&"x"));
    assert!(keys.contains(&"y"));
}

#[test]
fn test_object_in_sequence() {
    let events = parse("servers ({host a} {host b})");
    let obj_starts = events
        .iter()
        .filter(|e| matches!(e.kind, EventKind::ObjectStart))
        .count();
    // 3 = implicit root object + 2 objects in sequence
    assert_eq!(obj_starts, 3);
}

#[test]
fn test_attribute_values() {
    let events = parse("config name>app tags>(a b) opts>{x 1}");
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(value),
                ..
            } => Some(value.as_ref()),
            _ => None,
        })
        .collect();
    assert!(keys.contains(&"config"));
    assert!(keys.contains(&"name"));
    assert!(keys.contains(&"tags"));
    assert!(keys.contains(&"opts"));
    assert!(
        events
            .iter()
            .any(|e| matches!(e.kind, EventKind::SequenceStart))
    );
}

#[test]
fn test_too_many_atoms_with_attributes() {
    assert_parse_errors(
        r#"
spec selector matchLabels app>web tier>frontend
              ^^^^^^^^^^^ TooManyAtoms
"#,
    );
}

#[test]
fn test_attribute_no_spaces() {
    let events = parse("x > y");
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(value),
                ..
            } => Some(value.as_ref()),
            _ => None,
        })
        .collect();
    assert!(keys.contains(&"x"));
}

#[test]
fn test_explicit_root_after_comment() {
    let events = parse("// comment\n{a 1}");
    assert!(
        events
            .iter()
            .any(|e| matches!(e.kind, EventKind::ObjectStart))
    );
    assert!(
        events.iter().any(
            |e| matches!(&e.kind, EventKind::Key { payload: Some(value), .. } if value == "a")
        )
    );
}

#[test]
fn test_explicit_root_after_doc_comment() {
    let events = parse("/// doc comment\n{a 1}");
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::DocComment { .. }))
    );
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::ObjectStart))
    );
}

#[test]
fn test_duplicate_bare_key() {
    assert_parse_errors(
        r#"
{a 1, a 2}
      ^ DuplicateKey
"#,
    );
}

#[test]
fn test_duplicate_quoted_key() {
    assert_parse_errors(
        r#"
{"key" 1, "key" 2}
          ^^^^^ DuplicateKey
"#,
    );
}

#[test]
fn test_duplicate_key_escape_normalized() {
    assert_parse_errors(
        r#"
{"ab" 1, "a\u{62}" 2}
         ^^^^^^^^^ DuplicateKey
"#,
    );
}

#[test]
fn test_duplicate_unit_key() {
    assert_parse_errors(
        r#"
{@ 1, @ 2}
      ^ DuplicateKey
"#,
    );
}

#[test]
fn test_duplicate_tagged_key() {
    assert_parse_errors(
        r#"
{@foo 1, @foo 2}
         ^^^^ DuplicateKey
"#,
    );
}

#[test]
fn test_different_keys_ok() {
    assert_parse_errors(r#"{a 1, b 2, c 3}"#);
}

#[test]
fn test_duplicate_key_at_root() {
    assert_parse_errors(
        r#"
a 1
a 2
^ DuplicateKey
"#,
    );
}

#[test]
fn test_mixed_separators_allowed() {
    // Mixed separators (commas and newlines) are allowed
    assert_parse_errors(
        r#"
{a 1, b 2
c 3}
"#,
    );
    assert_parse_errors(
        r#"
{a 1
b 2, c 3}
"#,
    );
}

#[test]
fn test_consistent_comma_separators() {
    assert_parse_errors(r#"{a 1, b 2, c 3}"#);
}

#[test]
fn test_consistent_newline_separators() {
    assert_parse_errors(
        r#"{a 1
b 2
c 3}"#,
    );
}

#[test]
fn test_valid_tag_names() {
    assert_parse_errors("@foo");
    assert_parse_errors("@_private");
    assert_parse_errors("@my-tag");
    assert_parse_errors("@Type123");
}

#[test]
fn test_invalid_tag_name_starts_with_hyphen() {
    assert_parse_errors(
        r#"
x @-foo
  ^^^^^ InvalidTagName
"#,
    );
}

#[test]
fn test_invalid_tag_name_starts_with_dot() {
    assert_parse_errors(
        r#"
x @.foo
  ^^^^^ InvalidTagName
"#,
    );
}

#[test]
fn test_unicode_escape_4digit_accented() {
    let events = parse(r#"x "\u00E9""#);
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::Scalar { value, .. } if value == "é"))
    );
}

#[test]
fn test_unicode_escape_mixed() {
    let events = parse(r#"x "\u0048\u{65}\u006C\u{6C}\u006F""#);
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::Scalar { value, .. } if value == "Hello"))
    );
}

#[test]
fn test_invalid_escape_null() {
    assert_parse_errors(
        r#"
x "\0"
   ^^ InvalidEscape
"#,
    );
}

#[test]
fn test_invalid_escape_unknown() {
    assert_parse_errors(
        r#"
x "\q"
   ^^ InvalidEscape
"#,
    );
}

#[test]
fn test_invalid_escape_multiple() {
    assert_parse_errors(
        r#"
x "\0\q\?"
   ^^ InvalidEscape
     ^^ InvalidEscape
       ^^ InvalidEscape
"#,
    );
}

#[test]
fn test_valid_escapes_still_work() {
    let events = parse(r#"x "a\nb\tc\\d\"e""#);
    assert!(
        events.iter().any(
            |e| matches!(&e.kind, EventKind::Scalar { value, .. } if value == "a\nb\tc\\d\"e")
        )
    );
    assert_parse_errors(r#"x "a\nb\tc\\d\"e""#);
}

#[test]
fn test_invalid_escape_in_key() {
    assert_parse_errors(
        r#"
"\0" value
 ^^ InvalidEscape
"#,
    );
}

#[test]
fn test_simple_key_value_with_attributes() {
    let events = parse("server host>localhost port>8080");
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(value),
                ..
            } => Some(value.as_ref()),
            _ => None,
        })
        .collect();
    assert!(keys.contains(&"server"));
    assert!(keys.contains(&"host"));
    assert!(keys.contains(&"port"));
    assert_parse_errors(r#"server host>localhost port>8080"#);
}

#[test]
fn test_dotted_path_simple() {
    let events = parse("a.b value");
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(value),
                ..
            } => Some(value.as_ref()),
            _ => None,
        })
        .collect();
    assert_eq!(keys, vec!["a", "b"]);
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::ObjectStart))
    );
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::Scalar { value, .. } if value == "value"))
    );
    assert_parse_errors(r#"a.b value"#);
}

#[test]
fn test_dotted_path_three_segments() {
    let events = parse("a.b.c deep");
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(value),
                ..
            } => Some(value.as_ref()),
            _ => None,
        })
        .collect();
    assert_eq!(keys, vec!["a", "b", "c"]);
    let obj_starts: Vec<_> = events
        .iter()
        .filter(|e| matches!(&e.kind, EventKind::ObjectStart))
        .collect();
    // 3 = implicit root object + 2 from dotted path (a { b { c deep } })
    assert_eq!(obj_starts.len(), 3);
    assert_parse_errors(r#"a.b.c deep"#);
}

#[test]
fn test_dotted_path_with_implicit_unit() {
    let events = parse("a.b");
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(value),
                ..
            } => Some(value.as_ref()),
            _ => None,
        })
        .collect();
    assert_eq!(keys, vec!["a", "b"]);
    assert!(events.iter().any(|e| matches!(&e.kind, EventKind::Unit)));
}

#[test]
fn test_dotted_path_empty_segment() {
    assert_parse_errors(
        r#"
a..b value
^^^^ InvalidKey
"#,
    );
}

#[test]
fn test_dotted_path_trailing_dot() {
    assert_parse_errors(
        r#"
a.b. value
^^^^ InvalidKey
"#,
    );
}

#[test]
fn test_dotted_path_leading_dot() {
    assert_parse_errors(
        r#"
.a.b value
^^^^ InvalidKey
"#,
    );
}

#[test]
fn test_dotted_path_with_object_value() {
    let events = parse("a.b { c d }");
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(value),
                ..
            } => Some(value.as_ref()),
            _ => None,
        })
        .collect();
    assert!(keys.contains(&"a"));
    assert!(keys.contains(&"b"));
    assert!(keys.contains(&"c"));
    assert_parse_errors(r#"a.b { c d }"#);
}

#[test]
fn test_dotted_path_with_attributes_value() {
    let events = parse("selector.matchLabels app>web");
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(value),
                ..
            } => Some(value.as_ref()),
            _ => None,
        })
        .collect();
    assert!(keys.contains(&"selector"));
    assert!(keys.contains(&"matchLabels"));
    assert!(keys.contains(&"app"));
    assert_parse_errors(r#"selector.matchLabels app>web"#);
}

#[test]
fn test_dot_in_value_is_literal() {
    let events = parse("key example.com");
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(value),
                ..
            } => Some(value.as_ref()),
            _ => None,
        })
        .collect();
    assert_eq!(keys, vec!["key"]);
    assert!(
        events
            .iter()
            .any(|e| matches!(&e.kind, EventKind::Scalar { value, .. } if value == "example.com"))
    );
    assert_parse_errors(r#"key example.com"#);
}

#[test]
fn test_sibling_dotted_paths() {
    let events = parse("foo.bar.x value1\nfoo.bar.y value2\nfoo.baz value3");
    assert_parse_errors(
        r#"foo.bar.x value1
foo.bar.y value2
foo.baz value3"#,
    );
    let keys: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Key {
                payload: Some(value),
                ..
            } => Some(value.as_ref()),
            _ => None,
        })
        .collect();
    assert!(keys.contains(&"foo"));
    assert!(keys.contains(&"bar"));
    assert!(keys.contains(&"baz"));
    assert!(keys.contains(&"x"));
    assert!(keys.contains(&"y"));
}

#[test]
fn test_reopen_closed_path_error() {
    assert_parse_errors(
        r#"
foo.bar {}
foo.baz {}
foo.bar.x value
^^^^^^^^^ ReopenedPath
"#,
    );
}

#[test]
fn test_reopen_nested_closed_path_error() {
    assert_parse_errors(
        r#"
a.b.c {}
a.b.d {}
a.x {}
a.b.e {}
^^^^^ ReopenedPath
"#,
    );
}

#[test]
fn test_nest_into_scalar_error() {
    assert_parse_errors(
        r#"
a.b value
a.b.c deep
^^^^^ NestIntoTerminal
"#,
    );
}

#[test]
fn test_different_top_level_paths_ok() {
    assert_parse_errors(
        r#"server.host localhost
database.port 5432"#,
    );
}

#[test]
fn test_bare_key_requires_whitespace_before_brace() {
    assert_parse_errors(
        r#"
config{}
      ^ MissingWhitespaceBeforeBlock
"#,
    );
}

#[test]
fn test_bare_key_requires_whitespace_before_paren() {
    assert_parse_errors(
        r#"
items(1 2 3)
     ^ MissingWhitespaceBeforeBlock
"#,
    );
}

#[test]
fn test_bare_key_with_whitespace_before_brace_ok() {
    assert_parse_errors("config {}");
}

#[test]
fn test_bare_key_with_whitespace_before_paren_ok() {
    assert_parse_errors("items (1 2 3)");
}

#[test]
fn test_tag_with_brace_no_whitespace_ok() {
    assert_parse_errors("config @object{}");
}

#[test]
fn test_quoted_key_no_whitespace_ok() {
    assert_parse_errors(r#""config"{}"#);
}

#[test]
fn test_minified_styx_with_whitespace() {
    assert_parse_errors("{server {host localhost,port 8080}}");
}

#[test]
fn test_invalid_escape_annotated() {
    assert_parse_errors(
        r#"
x "\0"
   ^^ InvalidEscape
"#,
    );
}

#[test]
fn test_invalid_tag_name_annotated() {
    assert_parse_errors(
        r#"
x @123
  ^^^^ InvalidTagName
"#,
    );
}

#[test]
fn test_dangling_doc_comment_annotated() {
    assert_parse_errors(
        r#"
foo bar
/// dangling
^^^^^^^^^^^^ DanglingDocComment
"#,
    );
}

#[test]
fn test_nested_tag_in_object() {
    // Complex nested structure with unit keys and tag values
    let input = r#"meta {id test}
schema {
    @ @object{
        name @optional(@string)
    }
}"#;
    let events = parse(input);
    let errors: Vec<_> = events
        .iter()
        .filter(|e| matches!(e.kind, EventKind::Error { .. }))
        .collect();
    assert!(errors.is_empty(), "Unexpected errors: {:?}", errors);
}

#[test]
fn test_tag_with_seq_containing_tag() {
    // Tag with sequence payload containing another tag
    let input = "x @outer(@inner)";
    let events = parse(input);
    let errors: Vec<_> = events
        .iter()
        .filter(|e| matches!(e.kind, EventKind::Error { .. }))
        .collect();
    assert!(errors.is_empty(), "Unexpected errors: {:?}", errors);
}

#[test]
fn test_tag_with_typed_literal_in_seq() {
    // This is the format used by schema_gen: @default(true @bool)
    // It's a sequence with two elements: true and @bool (unit tag)
    // We wrap it in an entry to test it as a value
    let input = "key @default(true @bool)";
    let events = parse(input);
    let errors: Vec<_> = events
        .iter()
        .filter(|e| matches!(e.kind, EventKind::Error { .. }))
        .collect();
    assert!(errors.is_empty(), "Unexpected errors: {:?}", errors);
}

#[test]
fn test_schema_with_comma_separated_entries() {
    // This is from the schema_gen tests - multiple entries in an object separated by commas
    let input = r#"schema {@ @object{inner @Inner}, Inner @object{enabled @bool}}"#;
    let events = parse(input);
    for event in &events {
        eprintln!("{:?}", event);
    }
    let errors: Vec<_> = events
        .iter()
        .filter(|e| matches!(e.kind, EventKind::Error { .. }))
        .collect();
    assert!(errors.is_empty(), "Unexpected errors: {:?}", errors);
}

#[test]
fn test_tag_unit_object_is_too_many_atoms() {
    // @a @ {} is three atoms: @a (tag), @ (unit), {} (object)
    // An entry can only have key + value (2 atoms max), so this is TooManyAtoms
    let input = "@a @ {}";
    let events = parse(input);

    let errors: Vec<_> = events
        .iter()
        .filter_map(|e| match &e.kind {
            EventKind::Error { kind } => Some(kind),
            _ => None,
        })
        .collect();
    assert_eq!(errors.len(), 1);
    assert!(matches!(errors[0], ParseErrorKind::TooManyAtoms));
}

#[test]
fn test_explicit_object_is_only_one_struct_start() {
    let input = "{key val}";
    let events = parse(input);
    assert_events_eq!(
        input,
        events,
        "
        DocumentStart
        ObjectStart
        EntryStart
        Key(\"key\")
        Scalar(\"val\")
        EntryEnd
        ObjectEnd
        DocumentEnd
        "
    );
}

#[test]
fn test_unclosed_explicit_object() {
    let input = "{key val";
    let events = parse(input);
    assert_events_eq!(
        input,
        events,
        "
        DocumentStart
        ObjectStart
        EntryStart
        Key(\"key\")
        Scalar(\"val\")
        EntryEnd
        Error(UnclosedObject)
        ObjectEnd
        DocumentEnd
        "
    );
}

#[test]
fn test_implicit_object_has_object_start() {
    let input = "key val";
    let events = parse(input);
    assert_events_eq!(
        input,
        events,
        "
        DocumentStart
        ObjectStart
        EntryStart
        Key(\"key\")
        Scalar(\"val\")
        EntryEnd
        ObjectEnd
        DocumentEnd
        "
    );
}
