#![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;

const TEST_CASES: &[(&str, &str)] = &[
    // P0057 - VarEditorSpecifierExpected
    ("files/P0057_01.uc", "var(Category,) int A;\n"),
    ("files/P0057_02.uc", "var(,) int A;\n"),
    (
        "files/P0057_03.uc",
        "var(\n    ,\n    ,\n    Category\n) int A;\n",
    ),
    (
        "files/P0057_04.uc",
        "var(\n    Category,\n\n\n) int A;\n",
    ),

    // P0058 - VarEditorSpecifierListMissingSeparator
    ("files/P0058_01.uc", "var(Category DisplayName) int A;\n"),
    ("files/P0058_02.uc", "var(\"Display\" \"Tooltip\") int A;\n"),
    (
        "files/P0058_03.uc",
        "var(\n    Category\n    DisplayName\n) int A;\n",
    ),
    (
        "files/P0058_04.uc",
        "var(\n    Category\n\n    \"Display Name\",\n    Advanced\n) int A;\n",
    ),

    // P0059 - VarEditorSpecifierListExpectedCommaOrClosingParenthesis
    ("files/P0059_01.uc", "var(Category :) int A;\n"),
    ("files/P0059_02.uc", "var(\"Display\" *) int A;\n"),
    (
        "files/P0059_03.uc",
        "var(\n    Category\n    [\n) int A;\n",
    ),
    (
        "files/P0059_04.uc",
        "var(\n    \"Display Name\"\n\n    ]\n) int A;\n",
    ),

    // P0060 - VarEditorSpecifierListMissingClosingParenthesis
    ("files/P0060_01.uc", "var(Category\n"),
    (
        "files/P0060_02.uc",
        "var(\n    Category,\n    \"Display Name\"\n",
    ),
    (
        "files/P0060_03.uc",
        "var(\n\n\n",
    ),
    (
        "files/P0060_04.uc",
        "var(\n    Category,\n\n    \"Display Name\"\n\n",
    ),
];

/// 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)] = &[
    // P0061 - TypeSpecifierExpected
    ("files/P0061_01.uc", "{\n    local ;\n}\n"),
    (
        "files/P0061_02.uc",
        "{\n    local\n        ;\n}\n",
    ),
    (
        "files/P0061_03.uc",
        "{\n    local\n        [\n        ] Values;\n}\n",
    ),
    (
        "files/P0061_04.uc",
        "{\n    local\n",
    ),

    // P0062 - NamedTypeInvalidName
    ("files/P0062_01.uc", "{\n    local Foo. ;\n}\n"),
    (
        "files/P0062_02.uc",
        "{\n    local\n        Foo.\n        ;\n}\n",
    ),
    (
        "files/P0062_03.uc",
        "{\n    local Foo..Bar Value;\n}\n",
    ),
    (
        "files/P0062_04.uc",
        "{\n    local\n        SomePackage\n        .\n",
    ),

    // P0063 - ArrayTypeMissingOpeningAngleBracket
    ("files/P0063_01.uc", "{\n    local array Values;\n}\n"),
    (
        "files/P0063_02.uc",
        "{\n    local\n        array\n        Values;\n}\n",
    ),
    ("files/P0063_03.uc", "{\n    local array[] Values;\n}\n"),
    (
        "files/P0063_04.uc",
        "{\n    local\n        array",
    ),

    // P0064 - ArrayTypeMissingClosingAngleBracket
    ("files/P0064_01.uc", "{\n    local array<int Values;\n}\n"),
    (
        "files/P0064_02.uc",
        "{\n    local\n        array<\n            int\n        Values;\n}\n",
    ),
    (
        "files/P0064_03.uc",
        "{\n    local array<class<Actor> ActorClasses;\n}\n",
    ),
    (
        "files/P0064_04.uc",
        "{\n    local\n        array<\n            string",
    ),

    // P0065 - ArrayTypeMissingElementType
    ("files/P0065_01.uc", "{\n    local array<> Values;\n}\n"),
    (
        "files/P0065_02.uc",
        "{\n    local\n        array<>\n        Values;\n}\n",
    ),
    (
        "files/P0065_03.uc",
        "{\n    local array<\n    > Values;\n}\n",
    ),
    (
        "files/P0065_04.uc",
        "{\n    local array<array<> > NestedValues;\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>"));
}
