use tb_frontend::{editing::*, lexer, parser};
#[test]
fn normalization_protects_tokens_and_is_idempotent() {
    for source in [
        "  print   Wort$; \"MiX  Case\" ' Kommentar  bleibt",
        "data  MiXeD,  frei  Text,\"Z\": print   1",
        "if   x% then   print x%",
        "rem frei  text",
        "print &Hff; 2.0#",
    ] {
        let normalized = normalize_line(source);
        assert_eq!(normalize_line(&normalized), normalized);
        let tokens = |s| {
            lexer::lex(s)
                .tokens
                .into_iter()
                .map(|t| t.kind)
                .collect::<Vec<_>>()
        };
        assert_eq!(tokens(source), tokens(&normalized));
    }
    assert_eq!(
        normalize_line("print   Wert$; \"MiX\" ' Kommentar"),
        "PRINT Wert$; \"MiX\" ' Kommentar"
    );
    assert!(normalize_line("data  MiXeD,  frei  Text").ends_with("  MiXeD,  frei  Text"));
}
#[test]
fn incomplete_is_parser_context_not_a_second_grammar() {
    for source in ["SUB X\n", "IF 1 THEN\n", "FUNCTION X\n"] {
        assert!(
            parser::parse("M", &lexer::lex(source).tokens).incomplete,
            "{source}"
        );
    }
    for source in ["PRINT )\n", "SUB X\nPRINT )\n", "PRINT 1\n"] {
        assert!(
            !parser::parse("M", &lexer::lex(source).tokens).incomplete,
            "{source}"
        );
    }
}
#[test]
fn declaration_maintenance_and_procedure_generation_are_transactional() {
    let source = "DEFINT A-Z\nSUB Work (x)\nx=x+1\nEND SUB\n";
    let (updated, notice) = maintain_declarations(source);
    assert!(notice.is_none(), "{notice:?}");
    assert!(updated.contains("DECLARE SUB WORK (X%)"), "{updated}");
    assert_eq!(maintain_declarations(&updated), (updated.clone(), None));
    for source in [
        "SUB Draft\n",
        "PRINT )\n",
        "DECLARE SUB Work(x$)\nSUB Work(x%)\nEND SUB\n",
    ] {
        let (same, notice) = maintain_declarations(source);
        assert_eq!(same, source);
        assert!(notice.is_some(), "{source}");
    }
    let manual = "DECLARE SUB Work(x AS INTEGER)\nSUB Work(x%)\nEND SUB\n";
    assert_eq!(maintain_declarations(manual), (manual.into(), None));
    let addition = new_procedure(source, source.len(), "Fresh", true).unwrap();
    assert!(addition.contains("FUNCTION Fresh%"));
    assert!(
        tb_frontend::analyze_source("M", &format!("{source}{addition}"))
            .diagnostics
            .is_empty()
    );
    for name in ["Work", "PRINT", "bad name", ""] {
        assert!(new_procedure(source, 0, name, false).is_err());
    }
}

#[test]
fn deftype_context_does_not_leak_from_another_procedure() {
    let source =
        "' Modulrumpf\nDEFINT A-Z\nSUB First\nDEFSTR A-Z\nx$=\"x\"\nEND SUB\nSUB Second\nEND SUB\n";
    let inside = source.find("x$=").unwrap();
    assert!(new_procedure(source, inside, "Fresh", true)
        .unwrap()
        .contains("FUNCTION Fresh$"));
    let second = source.find("SUB Second").unwrap();
    assert!(new_procedure(source, second, "Fresh", true)
        .unwrap()
        .contains("FUNCTION Fresh%"));
    let before = new_procedure(source, 0, "Fresh", true).unwrap();
    assert!(before.contains("FUNCTION Fresh!"));
    assert!(before.contains("DEFSNG A-Z"));
    for at in [inside, second, 0] {
        let addition = new_procedure(source, at, "Fresh", true).unwrap();
        let diagnostics =
            tb_frontend::analyze_source("M", &format!("{source}{addition}")).diagnostics;
        assert!(diagnostics.is_empty(), "{diagnostics:?}");
    }
    assert!(new_procedure(source, 0, "Foo ' comment", false).is_err());
}
