use nexus_lang::{Scanner, TokenKind};
use pretty_assertions::assert_eq;

/// Test a single token.
#[track_caller]
fn test_single(source: &str, expected: TokenKind) {
    let scanner = Scanner::new(source.into());
    let tokens = scanner.scan().expect("Scanning failed.");

    assert_eq!(tokens.len(), 1);

    let token = tokens
        .first()
        .expect("Expected at least one token, got none");

    assert_eq!(token.kind, expected);
    assert_eq!(token.lexeme, source); // We only test a single token.
}

/// Test multiple tokens.
#[track_caller]
fn test_multiple(source: &str, expected: &[TokenKind]) {
    let scanner = Scanner::new(source.into());
    let tokens = scanner.scan().expect("Scanning failed.");

    // Check token count match.
    assert_eq!(
        tokens.len(),
        expected.len(),
        "\nToken count mismatch for source: '{}'\nExpected {} tokens: {:?}\nGot      {} tokens: {:?}",
        source,
        expected.len(),
        expected,
        tokens.len(),
        tokens.iter().map(|t| &t.kind).collect::<Vec<_>>()
    );

    // Check each token matches expected.
    for (i, (token, expected_token)) in tokens.into_iter().zip(expected.iter()).enumerate() {
        assert_eq!(
            token.kind, *expected_token,
            "\nToken mismatch at position {} for source: '{}'\nExpected: {:?}\nActual: {:?}\nFull expected tokens: {:?}",
            i, source, expected_token, token.kind, expected
        );
    }
}

/// Test that scanning results in an error.
#[track_caller]
fn test_error(source: &str) {
    let scanner = Scanner::new(source.into());
    let result = scanner.scan();

    if let Ok(tokens) = &result {
        panic!(
            "Expected scan error for input '{source}', but scan succeeded with tokens: {:?}",
            tokens.iter().map(|t| &t.kind).collect::<Vec<_>>()
        );
    }
}

#[test]
fn basic_single_token() {
    test_single("(", TokenKind::LParen);
    test_single(")", TokenKind::RParen);
    test_single("{", TokenKind::LBrace);
    test_single("}", TokenKind::RBrace);
    test_single("[", TokenKind::LBracket);
    test_single("]", TokenKind::RBracket);
    test_single(":", TokenKind::Colon);
    test_single(";", TokenKind::Semicolon);
    test_single("&", TokenKind::Amp);
    test_single("+", TokenKind::Plus);
    test_single("-", TokenKind::Minus);
    test_single("->", TokenKind::Arrow);
    test_single("*", TokenKind::Star);
    test_single("/", TokenKind::Slash);
    test_single("\\", TokenKind::Backslash);
    test_single("%", TokenKind::Percent);
    test_single(",", TokenKind::Comma);
    test_single(".", TokenKind::Dot);
    test_single("..", TokenKind::Range);
    test_single("_", TokenKind::Underscore);
    test_single("=", TokenKind::Is);
    test_single("==", TokenKind::Eq);
    test_single(">", TokenKind::Gt);
    test_single(">=", TokenKind::GtEq);
    test_single("<", TokenKind::Lt);
    test_single("<=", TokenKind::LtEq);
    test_single("!", TokenKind::Bang);
    test_single("!=", TokenKind::NotEq);
    test_single("&&", TokenKind::And);
    test_single("||", TokenKind::DoublePipe);
    test_single("|", TokenKind::Pipe);
}

#[test]
fn basic_keyword() {
    test_single("true", TokenKind::True);
    test_single("false", TokenKind::False);
    test_single("const", TokenKind::Const);
    test_single("let", TokenKind::Let);
    test_single("mut", TokenKind::Mut);
    test_single("fn", TokenKind::Function);
    test_single("if", TokenKind::If);
    test_single("else", TokenKind::Else);
    test_single("for", TokenKind::For);
    test_single("in", TokenKind::In);
    test_single("while", TokenKind::While);
    test_single("return", TokenKind::Return);
    test_single("use", TokenKind::Use);
    test_single("bool", TokenKind::BoolId);
    test_single("Node", TokenKind::NodeId);
    test_single("Group", TokenKind::GroupId);
    test_single("Number", TokenKind::NumberId);
    test_single("String", TokenKind::StringId);
    test_single("port", TokenKind::Port);
    test_single("print", TokenKind::Print);
    test_single("node", TokenKind::Node);
    test_single("group", TokenKind::Group);
    test_single("self", TokenKind::Self_);
}

#[test]
fn self_keyword() {
    // 'self' always scans as Self_, regardless of position.
    test_single("self", TokenKind::Self_);

    // Member declaration: self.name = node Foo;
    test_multiple(
        "self.src = node Source;",
        &[
            TokenKind::Self_,
            TokenKind::Dot,
            TokenKind::Identifier("src".to_owned()),
            TokenKind::Is,
            TokenKind::Node,
            TokenKind::Identifier("Source".to_owned()),
            TokenKind::Semicolon,
        ],
    );

    // Member access for a port reference: self.src.output
    test_multiple(
        "self.src.output",
        &[
            TokenKind::Self_,
            TokenKind::Dot,
            TokenKind::Identifier("src".to_owned()),
            TokenKind::Dot,
            TokenKind::Identifier("output".to_owned()),
        ],
    );

    // Connection with self on both sides: self.a.out -> self.b.in;
    test_multiple(
        "self.src.output -> self.sink.input;",
        &[
            TokenKind::Self_,
            TokenKind::Dot,
            TokenKind::Identifier("src".to_owned()),
            TokenKind::Dot,
            TokenKind::Identifier("output".to_owned()),
            TokenKind::Arrow,
            TokenKind::Self_,
            TokenKind::Dot,
            TokenKind::Identifier("sink".to_owned()),
            TokenKind::Dot,
            TokenKind::Identifier("input".to_owned()),
            TokenKind::Semicolon,
        ],
    );

    // self inside a group body alongside ordinary let bindings.
    test_multiple(
        "group { self.n = node Foo; let x = 1; }",
        &[
            TokenKind::Group,
            TokenKind::LBrace,
            TokenKind::Self_,
            TokenKind::Dot,
            TokenKind::Identifier("n".to_owned()),
            TokenKind::Is,
            TokenKind::Node,
            TokenKind::Identifier("Foo".to_owned()),
            TokenKind::Semicolon,
            TokenKind::Let,
            TokenKind::Identifier("x".to_owned()),
            TokenKind::Is,
            TokenKind::Number(1.0),
            TokenKind::Semicolon,
            TokenKind::RBrace,
        ],
    );

    // 'self' outside a group body still scans as Self_ (contextual restriction is semantic).
    test_multiple(
        "let p = self.src;",
        &[
            TokenKind::Let,
            TokenKind::Identifier("p".to_owned()),
            TokenKind::Is,
            TokenKind::Self_,
            TokenKind::Dot,
            TokenKind::Identifier("src".to_owned()),
            TokenKind::Semicolon,
        ],
    );
}

#[test]
fn single_line_comment_test() {
    test_multiple("// Comment.", &[]);
    test_multiple("// let x = 0;", &[]);
    test_multiple(
        "let x; // Comment.",
        &[
            TokenKind::Let,
            TokenKind::Identifier("x".to_owned()),
            TokenKind::Semicolon,
        ],
    );
}

#[test]
fn multi_line_comment() {
    // On a single line.
    test_multiple("/* Comment. */", &[]);
    test_multiple("/* let x = 0; */", &[]);
    test_multiple(
        "let x; /* Comment. */",
        &[
            TokenKind::Let,
            TokenKind::Identifier("x".to_owned()),
            TokenKind::Semicolon,
        ],
    );
    test_multiple(
        "/* Comment. */ let x;",
        &[
            TokenKind::Let,
            TokenKind::Identifier("x".to_owned()),
            TokenKind::Semicolon,
        ],
    );

    // On multiple lines.
    test_multiple("/*\nComment.\n*/", &[]);
    test_multiple("/*\nlet x = 0;\n*/", &[]);
    test_multiple(
        "let x; /*\nComment.\n*/",
        &[
            TokenKind::Let,
            TokenKind::Identifier("x".to_owned()),
            TokenKind::Semicolon,
        ],
    );
    test_multiple("/* comment * more */", &[]);
    test_multiple("/*\n3*3/9\n*/", &[]);

    // Nested comment.
    test_multiple("/* /* inner */ outer */", &[]);
}

#[test]
fn valid_string() {
    // Basic strings.
    test_single("\"\"", TokenKind::String(String::new()));
    test_single("\"hello\"", TokenKind::String("hello".to_owned()));
    test_single("\"world\"", TokenKind::String("world".to_owned()));
    test_single(
        "\"Hello, World!\"",
        TokenKind::String("Hello, World!".to_owned()),
    );

    // Strings with numbers and symbols.
    test_single("\"123\"", TokenKind::String("123".to_owned()));
    test_single("\"test123\"", TokenKind::String("test123".to_owned()));
    test_single("\"!@#$%^&*()\"", TokenKind::String("!@#$%^&*()".to_owned()));
    test_single(
        "\"spaces   here\"",
        TokenKind::String("spaces   here".to_owned()),
    );

    // Simple escape sequences.
    test_single("\"\\\"\"", TokenKind::String("\"".to_owned()));
    test_single("\"\\\\\"", TokenKind::String("\\".to_owned()));
    test_single("\"\\n\"", TokenKind::String("\n".to_owned()));
    test_single("\"\\r\"", TokenKind::String("\r".to_owned()));
    test_single("\"\\t\"", TokenKind::String("\t".to_owned()));
    test_single("\"\\b\"", TokenKind::String("\u{0008}".to_owned()));
    test_single("\"\\f\"", TokenKind::String("\u{000C}".to_owned()));
    test_single("\"\\0\"", TokenKind::String("\0".to_owned()));

    // Mixed content with escapes.
    test_single(
        "\"Hello\\nWorld\"",
        TokenKind::String("Hello\nWorld".to_owned()),
    );
    test_single(
        "\"Path\\\\to\\\\file\"",
        TokenKind::String("Path\\to\\file".to_owned()),
    );
    test_single(
        "\"Quote: \\\"text\\\"\"",
        TokenKind::String("Quote: \"text\"".to_owned()),
    );
    test_single("\"Tab\\there\"", TokenKind::String("Tab\there".to_owned()));

    // Hex escape sequences.
    test_single("\"\\x41\"", TokenKind::String("A".to_owned())); // 0x41 = 'A'.
    test_single("\"\\x20\"", TokenKind::String(" ".to_owned())); // 0x20 = space.
    test_single("\"\\x00\"", TokenKind::String("\0".to_owned())); // 0x00 = null.
    test_single("\"\\xFF\"", TokenKind::String("\u{FF}".to_owned())); // 0xFF = 255.
    test_single(
        "\"Hello\\x20World\"",
        TokenKind::String("Hello World".to_owned()),
    );

    // Unicode escape sequences (\u).
    test_single("\"\\u0041\"", TokenKind::String("A".to_owned())); // U+0041 = 'A'.
    test_single("\"\\u0020\"", TokenKind::String(" ".to_owned())); // U+0020 = space.
    test_single("\"\\u03B1\"", TokenKind::String("α".to_owned())); // U+03B1 = Greek alpha.
    test_single("\"\\u2603\"", TokenKind::String("☃".to_owned())); // U+2603 = snowman.
    test_single(
        "\"Hello\\u0020World\"",
        TokenKind::String("Hello World".to_owned()),
    );

    // Unicode escape sequences (\U).
    test_single("\"\\U00000041\"", TokenKind::String("A".to_owned())); // U+00000041 = 'A'.
    test_single("\"\\U00000020\"", TokenKind::String(" ".to_owned())); // U+00000020 = space.
    test_single("\"\\U000003B1\"", TokenKind::String("α".to_owned())); // U+000003B1 = Greek alpha.
    test_single("\"\\U00002603\"", TokenKind::String("☃".to_owned())); // U+00002603 = snowman.
    test_single("\"\\U0001F600\"", TokenKind::String("😀".to_owned())); // U+0001F600 = grinning face.

    // Mixed escape types.
    test_single(
        "\"\\x41\\u0042\\U00000043\"",
        TokenKind::String("ABC".to_owned()),
    );
    test_single("\"\\n\\t\\r\"", TokenKind::String("\n\t\r".to_owned()));
    test_single(
        "\"\\\"Hello\\\"\\n\"",
        TokenKind::String("\"Hello\"\n".to_owned()),
    );

    // Edge cases.
    test_single("\"\\x0A\"", TokenKind::String("\n".to_owned())); // 0x0A = newline.
    test_single("\"\\u000A\"", TokenKind::String("\n".to_owned())); // U+000A = newline.
    test_single("\"\\U0000000A\"", TokenKind::String("\n".to_owned())); // U+0000000A = newline.
}

#[test]
fn invalid_string() {
    // Unterminated strings.
    test_error("\"");
    test_error("\"hello");
    test_error("\"hello world");
    test_error("\"unterminated string with escape\\n");
    test_error("\"unterminated with quote\\\"");

    // Invalid escape sequences.
    test_error("\"\\a\""); // Invalid escape character.
    test_error("\"\\z\""); // Invalid escape character.
    test_error("\"\\1\""); // Invalid escape character.
    test_error("\"\\!\""); // Invalid escape character.
    test_error("\"\\@\""); // Invalid escape character.
    test_error("\"\\#\""); // Invalid escape character.

    // Incomplete escape sequences.
    test_error("\"\\\""); // Backslash at end.
    test_error("\"hello\\\""); // Backslash at end.

    // Invalid hex escape sequences.
    test_error("\"\\x\""); // Missing hex digits.
    test_error("\"\\x4\""); // Only one hex digit.
    test_error("\"\\xGG\""); // Invalid hex digits.
    test_error("\"\\x4G\""); // One valid, one invalid hex digit.
    test_error("\"\\xZ1\""); // Invalid hex digits.
    test_error("\"\\x!@\""); // Invalid hex digits.

    // Invalid unicode escape sequences (\u).
    test_error("\"\\u\""); // Missing hex digits.
    test_error("\"\\u4\""); // Only one hex digit.
    test_error("\"\\u41\""); // Only two hex digits.
    test_error("\"\\u414\""); // Only three hex digits.
    test_error("\"\\uGGGG\""); // Invalid hex digits.
    test_error("\"\\u41GG\""); // Mixed valid/invalid hex digits.
    test_error("\"\\u!@#$\""); // Invalid hex digits.

    // Invalid unicode escape sequences (\U).
    test_error("\"\\U\""); // Missing hex digits.
    test_error("\"\\U4\""); // Only one hex digit.
    test_error("\"\\U41\""); // Only two hex digits.
    test_error("\"\\U414\""); // Only three hex digits.
    test_error("\"\\U4141\""); // Only four hex digits.
    test_error("\"\\U41414\""); // Only five hex digits.
    test_error("\"\\U414141\""); // Only six hex digits.
    test_error("\"\\U4141414\""); // Only seven hex digits.
    test_error("\"\\UGGGGGGGG\""); // Invalid hex digits.
    test_error("\"\\U414141GG\""); // Mixed valid/invalid hex digits.
    test_error("\"\\U1234567G\""); // Invalid unicode escape.
    test_error("\"\\U!@#$%^&*\""); // Invalid hex digits.

    // Strings with newlines (not escaped).
    test_error("\"hello\nworld\""); // Actual newline in string.
    test_error("\"line1\nline2\""); // Actual newline in string.
    test_error("\"hello\rworld\""); // Actual carriage return in string.

    // Mixed invalid cases.
    test_error("\"valid start\\x"); // Valid start but incomplete escape.
    test_error("\"valid start\\u123"); // Valid start but incomplete escape.
    test_error("\"valid start\\U1234567"); // Valid start but incomplete escape.
    test_error("\"escape\\x4Gthen more\""); // Invalid hex then more content.
}

#[test]
fn valid_number() {
    let test_single_number = |s: &str, n: f64| {
        test_single(s, TokenKind::Number(n));
    };

    (0..=9).for_each(|n| test_single_number(&n.to_string(), f64::from(n)));

    test_single_number("42", 42.0);
    test_single_number("123", 123.0);
    test_single_number("1000", 1000.0);
    test_single_number("999999", 999999.0);

    test_single_number("0.0", 0.0);
    test_single_number("1.0", 1.0);
    test_single_number("0.5", 0.5);
    test_single_number("3.14", 3.14);
    test_single_number("123.456", 123.456);
    test_single_number("0.123456789", 0.123456789);

    test_single_number("00", 0.0);
    test_single_number("01", 1.0);
    test_single_number("0123", 123.0);
    test_single_number("00.00", 0.0);
    test_single_number("01.23", 1.23);
}

#[test]
fn valid_number_with_range() {
    test_multiple(
        "1..10",
        &[
            TokenKind::Number(1.0),
            TokenKind::Range,
            TokenKind::Number(10.0),
        ],
    );
    test_multiple(
        "1 ..10",
        &[
            TokenKind::Number(1.0),
            TokenKind::Range,
            TokenKind::Number(10.0),
        ],
    );
    test_multiple(
        "1.. 10",
        &[
            TokenKind::Number(1.0),
            TokenKind::Range,
            TokenKind::Number(10.0),
        ],
    );
    test_multiple(
        "1..=10",
        &[
            TokenKind::Number(1.0),
            TokenKind::RangeInclusive,
            TokenKind::Number(10.0),
        ],
    );
    test_multiple(
        "1 ..=10",
        &[
            TokenKind::Number(1.0),
            TokenKind::RangeInclusive,
            TokenKind::Number(10.0),
        ],
    );
    test_multiple(
        "1.. =10",
        &[
            TokenKind::Number(1.0),
            TokenKind::Range,
            TokenKind::Is,
            TokenKind::Number(10.0),
        ],
    );
}

#[test]
fn invalid_number() {
    test_error("0.");
    test_error("1.");
    test_error("9.");
    test_error("42.");
    test_error("123.");
    test_error("1000.");
    test_error("00.");
    test_error("0123.");
}

#[test]
fn valid_identifiers() {
    test_single("x", TokenKind::Identifier("x".to_owned()));
    test_single("ɑ", TokenKind::Identifier("ɑ".to_owned()));
    test_single("ah", TokenKind::Identifier("ah".to_owned()));
    test_single("word", TokenKind::Identifier("word".to_owned()));
    test_single("CamelCase", TokenKind::Identifier("CamelCase".to_owned()));
    test_single("snake_case", TokenKind::Identifier("snake_case".to_owned()));
    test_single("ALLUPPER", TokenKind::Identifier("ALLUPPER".to_owned()));
    test_single("ŮñĭçøƋɇ", TokenKind::Identifier("ŮñĭçøƋɇ".to_owned()));
    test_single(
        "trailing_numbers012",
        TokenKind::Identifier("trailing_numbers012".to_owned()),
    );
    test_single(
        "numbers1n8etw33n",
        TokenKind::Identifier("numbers1n8etw33n".to_owned()),
    );
    test_single(
        "veeeeeeeerylooooooongwooooooord",
        TokenKind::Identifier("veeeeeeeerylooooooongwooooooord".to_owned()),
    );
}

#[test]
fn invalid_identifiers() {
    let test_error_result = |s| {
        let r = Scanner::new(s).scan();

        assert!(r.is_err(), "Expected unsuccessful scan result");
    };

    test_error_result("😜".into()); // Non-alphabetic.

    // Test unexpected characters.
    test_error("@");
    test_error("#");
    test_error("$");
    test_error("^");
    test_error("~");
    test_error("`");
}

#[test]
fn valid_sequence() {
    // Test 1: Variable declaration with assignment.
    test_multiple(
        "let x = 42;",
        &[
            TokenKind::Let,
            TokenKind::Identifier("x".to_owned()),
            TokenKind::Is,
            TokenKind::Number(42.0),
            TokenKind::Semicolon,
        ],
    );

    // Test 2: Function declaration with parameters and arrow return type.
    test_multiple(
        "fn add(a, b) -> Number { return a + b; }",
        &[
            TokenKind::Function,
            TokenKind::Identifier("add".to_owned()),
            TokenKind::LParen,
            TokenKind::Identifier("a".to_owned()),
            TokenKind::Comma,
            TokenKind::Identifier("b".to_owned()),
            TokenKind::RParen,
            TokenKind::Arrow,
            TokenKind::NumberId,
            TokenKind::LBrace,
            TokenKind::Return,
            TokenKind::Identifier("a".to_owned()),
            TokenKind::Plus,
            TokenKind::Identifier("b".to_owned()),
            TokenKind::Semicolon,
            TokenKind::RBrace,
        ],
    );

    // Test 3: Conditional statement with comparison operators.
    test_multiple(
        "if x > 10 && y < 20 { print \"In range\"; }",
        &[
            TokenKind::If,
            TokenKind::Identifier("x".to_owned()),
            TokenKind::Gt,
            TokenKind::Number(10.0),
            TokenKind::And,
            TokenKind::Identifier("y".to_owned()),
            TokenKind::Lt,
            TokenKind::Number(20.0),
            TokenKind::LBrace,
            TokenKind::Print,
            TokenKind::String("In range".to_owned()),
            TokenKind::Semicolon,
            TokenKind::RBrace,
        ],
    );

    // Test 4: Loop with range operator.
    test_multiple(
        "for i in 1..5 { print i; }",
        &[
            TokenKind::For,
            TokenKind::Identifier("i".to_owned()),
            TokenKind::In,
            TokenKind::Number(1.0),
            TokenKind::Range,
            TokenKind::Number(5.0),
            TokenKind::LBrace,
            TokenKind::Print,
            TokenKind::Identifier("i".to_owned()),
            TokenKind::Semicolon,
            TokenKind::RBrace,
        ],
    );

    // Test 5: Node and Group keywords with properties.
    test_multiple(
        "node Circle { radius = 5.0; } group Shapes { circles = [Circle]; }",
        &[
            TokenKind::Node,
            TokenKind::Identifier("Circle".to_owned()),
            TokenKind::LBrace,
            TokenKind::Identifier("radius".to_owned()),
            TokenKind::Is,
            TokenKind::Number(5.0),
            TokenKind::Semicolon,
            TokenKind::RBrace,
            TokenKind::Group,
            TokenKind::Identifier("Shapes".to_owned()),
            TokenKind::LBrace,
            TokenKind::Identifier("circles".to_owned()),
            TokenKind::Is,
            TokenKind::LBracket,
            TokenKind::Identifier("Circle".to_owned()),
            TokenKind::RBracket,
            TokenKind::Semicolon,
            TokenKind::RBrace,
        ],
    );

    // Test 6: Boolean operations, equality and string concatenation.
    test_multiple(
        "let valid = true && !false; let msg = \"Status: \" + (valid == true);",
        &[
            TokenKind::Let,
            TokenKind::Identifier("valid".to_owned()),
            TokenKind::Is,
            TokenKind::True,
            TokenKind::And,
            TokenKind::Bang,
            TokenKind::False,
            TokenKind::Semicolon,
            TokenKind::Let,
            TokenKind::Identifier("msg".to_owned()),
            TokenKind::Is,
            TokenKind::String("Status: ".to_owned()),
            TokenKind::Plus,
            TokenKind::LParen,
            TokenKind::Identifier("valid".to_owned()),
            TokenKind::Eq,
            TokenKind::True,
            TokenKind::RParen,
            TokenKind::Semicolon,
        ],
    );

    // Test 7: Mutable variable with arithmetic operations.
    test_multiple(
        "let mut total = 100; total = total - 50; total = total * 2; total = total / 4;",
        &[
            TokenKind::Let,
            TokenKind::Mut,
            TokenKind::Identifier("total".to_owned()),
            TokenKind::Is,
            TokenKind::Number(100.0),
            TokenKind::Semicolon,
            TokenKind::Identifier("total".to_owned()),
            TokenKind::Is,
            TokenKind::Identifier("total".to_owned()),
            TokenKind::Minus,
            TokenKind::Number(50.0),
            TokenKind::Semicolon,
            TokenKind::Identifier("total".to_owned()),
            TokenKind::Is,
            TokenKind::Identifier("total".to_owned()),
            TokenKind::Star,
            TokenKind::Number(2.0),
            TokenKind::Semicolon,
            TokenKind::Identifier("total".to_owned()),
            TokenKind::Is,
            TokenKind::Identifier("total".to_owned()),
            TokenKind::Slash,
            TokenKind::Number(4.0),
            TokenKind::Semicolon,
        ],
    );

    // Test 8: Complex expression with nested parentheses and all arithmetic operators.
    test_multiple(
        "let result = (10 + 5) * ((20 - 8) / 4) % 3;",
        &[
            TokenKind::Let,
            TokenKind::Identifier("result".to_owned()),
            TokenKind::Is,
            TokenKind::LParen,
            TokenKind::Number(10.0),
            TokenKind::Plus,
            TokenKind::Number(5.0),
            TokenKind::RParen,
            TokenKind::Star,
            TokenKind::LParen,
            TokenKind::LParen,
            TokenKind::Number(20.0),
            TokenKind::Minus,
            TokenKind::Number(8.0),
            TokenKind::RParen,
            TokenKind::Slash,
            TokenKind::Number(4.0),
            TokenKind::RParen,
            TokenKind::Percent,
            TokenKind::Number(3.0),
            TokenKind::Semicolon,
        ],
    );
}

#[test]
fn invalid_sequence() {
    // Test 1: Unterminated string literal.
    test_error("let message = \"Hello, world");

    // Test 2: Invalid escape sequence in string.
    test_error("let message = \"Hello\\z world\"");

    // Test 3: Malformed number with trailing dot.
    test_error("let value = 42.");

    // Test 4: Unexpected character in the middle of valid code.
    test_error("let x = @10");

    // Test 5: Invalid character that's not allowed anywhere.
    test_error("let name = $variable");

    // Test 6: Invalid hex digits in string escape sequence.
    test_error("let invalid = \"\\xZG\";");

    // Test 7: Incomplete Unicode escape sequence.
    test_error("let invalid = \"\\u123\";"); // Should have 4 hex digits.

    // Test 8: Mix of valid tokens with an invalid character.
    test_error("fn main() { let x = 1 + 2 # 3; }");
}
