#![allow(
    clippy::all,
    clippy::pedantic,
    clippy::nursery,
    clippy::cargo,
    clippy::restriction
)]

use rottlib::arena::Arena;
use rottlib::diagnostics::Diagnostic;
use rottlib::lexer::TokenizedFile;
use rottlib::parser::Parser;

/// Lexer-focused fixtures.
///
/// Keep these small: the goal is to inspect lexer diagnostics and delimiter
/// recovery behavior, not full parser behavior.
const TEST_CASES: &[(&str, &str)] = &[
    // P0027: `else` without a matching `if`
    //
    // `else` cannot start a standalone block item. The parser should report
    // P0027 at `else`, then recover by bailing out of the current block.
    (
        "files/P0027_01.uc",
        "{\n    local bool bReady;\n    bReady = CheckReady();\n    else { StartMatch(); }\n    NotifyReady();\n}\n",
    ),

    // P0027: `case` outside of a `switch`
    //
    // `case` is a switch-arm boundary, not a valid statement or expression
    // starter in an ordinary block.
    (
        "files/P0027_02.uc",
        "{    local int Count;    Count = 3;    case 3:        Count++;    UpdateHud();}",
    ),

    // P0027: standalone `until` without a preceding `do`
    //
    // `until` is only meaningful as the tail of `do ... until`, so it should
    // not be accepted as a normal block item.
    (
        "files/P0027_03.uc",
        "{\n    local bool bDone;\n    bDone = false;\n    until (bDone)\n    TickWork();\n}\n",
    ),

    // P0027: preprocessor/exec directive inside a statement block
    //
    // `#exec` is declaration/top-level-like syntax, not a valid statement or
    // expression inside a braced statement block.
    (
        "files/P0027_04.uc",
        "{\n    local int Count;\n    Count = 0;\n    #exec TEXTURE IMPORT NAME=Bad FILE=Bad.bmp\n    Count++;\n}\n",
    ),
];

/// If true, also run the parser after tokenization.
///
/// For lexer-focused fixtures this is usually noisy, so keep it off unless you
/// want to inspect how parser recovery behaves after lexer diagnostics.
const RUN_PARSER: bool = true;

/// If true, print the parsed expression using Debug formatting.
const PRINT_PARSED_EXPR: bool = false;

const PRINT_LEXER_DIAGNOSTICS: bool = false;
const ALWAYS_PRINT_DIAGNOSTICS: bool = true;

fn main() {
    let arena = Arena::new();

    let mut had_any_problem = false;

    for (idx, (label, source)) in TEST_CASES.iter().enumerate() {
        println!("============================================================");
        println!("Case {}: {}", idx + 1, label);
        println!("------------------------------------------------------------");

        let tf = TokenizedFile::tokenize(source);

        let lexer_diagnostics = tf.diagnostics();

        if lexer_diagnostics.is_empty() {
            println!("Lexer diagnostics: none");
        } else {
            had_any_problem = true;

            if PRINT_LEXER_DIAGNOSTICS {
                println!("Lexer diagnostics:");
                for diag in lexer_diagnostics {
                    render_diagnostic(diag, &tf, Some(label), false);
                }
            }
        }

        if RUN_PARSER {
            let mut parser = Parser::new(&tf, &arena);
            let expr = parser.parse_expression();

            if PRINT_PARSED_EXPR {
                println!("Parsed expression:");
                println!("{expr:#?}");
            }

            if parser.diagnostics.is_empty() {
                println!("Parser diagnostics: none");
            } else {
                had_any_problem = true;

                if ALWAYS_PRINT_DIAGNOSTICS {
                    println!("Parser diagnostics:");
                    for diag in &parser.diagnostics {
                        render_diagnostic(diag, &tf, Some(label), false);
                    }
                }
            }
        }

        println!();
    }

    println!("============================================================");

    if had_any_problem {
        println!("Done. At least one case had lexer or parser diagnostics.");
        std::process::exit(1);
    } else {
        println!("Done. All cases completed without diagnostics.");
    }
}

fn render_diagnostic(
    diag: &Diagnostic,
    file: &TokenizedFile<'_>,
    file_name: Option<&str>,
    _colors: bool,
) {
    diag.render(file, file_name.unwrap_or("<default>"));
}