use super::*;

pub(super) const P0028_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0028_01.uc",
        source: "{\n    local Actor A;\n    A.\n}\n",
    },
    Fixture {
        label: "files/P0028_02.uc",
        source: "{\n    local Actor A;\n    A.;\n    Log(\"after\");\n}\n",
    },
    Fixture {
        label: "files/P0028_03.uc",
        source: "{\n    local Actor A;\n    A.\n    ;\n    Log(\"after\");\n}\n",
    },
    Fixture {
        label: "files/P0028_04.uc",
        source: "{\n    local Actor A;\n    Log(A.\n        );\n    Log(\"after\");\n}\n",
    },
];

pub(super) const P0029_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0029_01.uc",
        source: "{\n    local array<int> Values;\n    Values[];\n    Log(\"after\");\n}\n",
    },
    Fixture {
        label: "files/P0029_02.uc",
        source: "{\n    local array<int> Values;\n    Values[\n    ];\n    Log(\"after\");\n}\n",
    },
    Fixture {
        label: "files/P0029_03.uc",
        source: "{\n    local array<int> Values;\n    Values[, 1];\n    Log(\"after\");\n}\n",
    },
    Fixture {
        label: "files/P0029_04.uc",
        source: "{\n    local array<int> Values;\n    Log(Values[\n        ]);\n    Log(\"after\");\n}\n",
    },
];

pub(super) const P0030_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0030_01.uc",
        source: "{\n    local array<int> Values;\n    Values[0;\n    Log(\"after\");\n}\n",
    },
    Fixture {
        label: "files/P0030_02.uc",
        source: "{\n    local array<int> Values;\n    Values[\n        0\n    ;\n    Log(\"after\");\n}\n",
    },
    Fixture {
        label: "files/P0030_03.uc",
        source: "{\n    local array<int> Values;\n    Log(Values[0));\n    Log(\"after\");\n}\n",
    },
    Fixture {
        label: "files/P0030_04.uc",
        source: "{\n    local array<int> Values;\n    Values[GetIndex()\n    Values[1] = 7;\n}\n",
    },
];

#[test]
fn check_p0028_fixtures() {
    let runs = run_fixtures(P0028_FIXTURES);

    assert_eq!(runs.get("files/P0028_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0028_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0028_03.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0028_04.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0028_01.uc"),
        &ExpectedDiagnostic {
            headline: "expected member name after `.`, found `}`",
            severity: Severity::Error,
            code: Some("P0028"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(14),
                    end: TokenPosition(14),
                },
                message: "unexpected `}`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(12),
                    end: TokenPosition(12),
                },
                message: "after this `.`, a member name was expected",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0028_02.uc"),
        &ExpectedDiagnostic {
            headline: "expected member name after `.`, found `;`",
            severity: Severity::Error,
            code: Some("P0028"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(13),
                    end: TokenPosition(13),
                },
                message: "unexpected `;`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0028_03.uc"),
        &ExpectedDiagnostic {
            headline: "expected member name after `.`, found `;`",
            severity: Severity::Error,
            code: Some("P0028"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(15),
                    end: TokenPosition(15),
                },
                message: "unexpected `;`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(12),
                    end: TokenPosition(12),
                },
                message: "after this `.`, a member name was expected",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0028_04.uc"),
        &ExpectedDiagnostic {
            headline: "expected member name after `.`, found `)`",
            severity: Severity::Error,
            code: Some("P0028"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(17),
                    end: TokenPosition(17),
                },
                message: "unexpected `)`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(14),
                    end: TokenPosition(14),
                },
                message: "after this `.`, a member name was expected",
            }],
            help: None,
            notes: &[],
        },
    );
}

#[test]
fn check_p0029_fixtures() {
    let runs = run_fixtures(P0029_FIXTURES);

    assert_eq!(runs.get("files/P0029_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0029_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0029_03.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0029_04.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0029_01.uc"),
        &ExpectedDiagnostic {
            headline: "expected index expression after `[`, found `]`",
            severity: Severity::Error,
            code: Some("P0029"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(16),
                    end: TokenPosition(16),
                },
                message: "expected expression before `]`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0029_02.uc"),
        &ExpectedDiagnostic {
            headline: "expected index expression after `[`, found `]`",
            severity: Severity::Error,
            code: Some("P0029"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(15),
                    end: TokenPosition(18),
                },
                message: "expected expression before `]`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(15),
                    end: TokenPosition(15),
                },
                message: "after this `[`, an index expression was expected",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0029_03.uc"),
        &ExpectedDiagnostic {
            headline: "expected index expression after `[`, found `,`",
            severity: Severity::Error,
            code: Some("P0029"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(16),
                    end: TokenPosition(16),
                },
                message: "unexpected `,`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0029_04.uc"),
        &ExpectedDiagnostic {
            headline: "expected index expression after `[`, found `]`",
            severity: Severity::Error,
            code: Some("P0029"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(17),
                    end: TokenPosition(20),
                },
                message: "expected expression before `]`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(17),
                    end: TokenPosition(17),
                },
                message: "after this `[`, an index expression was expected",
            }],
            help: None,
            notes: &[],
        },
    );
}

#[test]
fn check_p0030_fixtures() {
    let runs = run_fixtures(P0030_FIXTURES);

    assert_eq!(runs.get("files/P0030_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0030_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0030_04.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0030_01.uc"),
        &ExpectedDiagnostic {
            headline: "missing `]` to close index selector",
            severity: Severity::Error,
            code: Some("P0030"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(17),
                    end: TokenPosition(17),
                },
                message: "expected `]` before `;`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0030_02.uc"),
        &ExpectedDiagnostic {
            headline: "missing `]` to close index selector",
            severity: Severity::Error,
            code: Some("P0030"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(15),
                    end: TokenPosition(21),
                },
                message: "expected `]` before `;`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(15),
                    end: TokenPosition(15),
                },
                message: "index selector starts here",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_by_code("files/P0030_03.uc", "P0030"),
        &ExpectedDiagnostic {
            headline: "missing `]` to close index selector",
            severity: Severity::Error,
            code: Some("P0030"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(19),
                    end: TokenPosition(19),
                },
                message: "expected `]` before `)`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0030_04.uc"),
        &ExpectedDiagnostic {
            headline: "missing `]` to close index selector",
            severity: Severity::Error,
            code: Some("P0030"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(15),
                    end: TokenPosition(21),
                },
                message: "expected `]` before `Values`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(15),
                    end: TokenPosition(15),
                },
                message: "index selector starts here",
            }],
            help: None,
            notes: &[],
        },
    );
}

pub(super) const P0031_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0031_01.uc",
        source: "{\n    Func(A B);\n    Log(\"after\");\n}\n",
    },
    Fixture {
        label: "files/P0031_02.uc",
        source: "{\n    Func\n    (A 123);\n    Log(\"after\");\n}\n",
    },
    Fixture {
        label: "files/P0031_03.uc",
        source: "{\n    Func(\n        A\n        new SomeClass\n    );\n    Log(\"after\");\n}\n",
    },
];

pub(super) const P0032_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0032_01.uc",
        source: "Func(",
    },
    Fixture {
        label: "files/P0032_02.uc",
        source: "Func\n(\n    A,",
    },
    Fixture {
        label: "files/P0032_03.uc",
        source: "Func(A,\n    B,",
    },
    Fixture {
        label: "files/P0032_04.uc",
        source: "{\n    Func\n    (\n        A,\n        B,\n",
    },
];

pub(super) const P0033_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0033_01.uc",
        source: "{\n    Func(A #, B);\n    Log(\"after\");\n}\n",
    },
    Fixture {
        label: "files/P0033_02.uc",
        source: "{\n    Func\n    (A\n        #,\n        B);\n    Log(\"after\");\n}\n",
    },
    Fixture {
        label: "files/P0033_03.uc",
        source: "{\n    Func(\n        A ?\n        , B\n    );\n    Log(\"after\");\n}\n",
    },
    Fixture {
        label: "files/P0033_04.uc",
        source: "{\n    Func\n    (\n        A\n        #,\n        B\n    );\n    Log(\"after\");\n}\n",
    },
];

#[test]
fn check_p0031_fixtures() {
    let runs = run_fixtures(P0031_FIXTURES);

    assert_eq!(runs.get("files/P0031_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0031_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0031_03.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0031_01.uc"),
        &ExpectedDiagnostic {
            headline: "missing `,` between function call arguments",
            severity: Severity::Error,
            code: Some("P0031"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(7),
                    end: TokenPosition(7),
                },
                message: "expected `,` before `B`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(5),
                    end: TokenPosition(5),
                },
                message: "previous argument ends here",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0031_02.uc"),
        &ExpectedDiagnostic {
            headline: "missing `,` between function call arguments",
            severity: Severity::Error,
            code: Some("P0031"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(9),
                    end: TokenPosition(9),
                },
                message: "expected `,` before `123`",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(3),
                        end: TokenPosition(3),
                    },
                    message: "function called here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(7),
                        end: TokenPosition(7),
                    },
                    message: "previous argument ends here",
                },
            ],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0031_03.uc"),
        &ExpectedDiagnostic {
            headline: "missing `,` between function call arguments",
            severity: Severity::Error,
            code: Some("P0031"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(10),
                    end: TokenPosition(10),
                },
                message: "expected `,` before `new`",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(4),
                        end: TokenPosition(4),
                    },
                    message: "function call argument list starts here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(7),
                        end: TokenPosition(7),
                    },
                    message: "previous argument ends here",
                },
            ],
            help: None,
            notes: &[],
        },
    );
}

#[test]
fn check_p0032_fixtures() {
    let runs = run_fixtures(P0032_FIXTURES);

    assert_eq!(runs.get("files/P0032_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0032_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0032_03.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0032_04.uc").unwrap().len(), 2);

    assert_diagnostic(
        &runs.get_any("files/P0032_01.uc"),
        &ExpectedDiagnostic {
            headline: "missing `)` to close function call argument list",
            severity: Severity::Error,
            code: Some("P0032"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(2),
                    end: TokenPosition(2),
                },
                message: "expected `)` before end of file",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0032_02.uc"),
        &ExpectedDiagnostic {
            headline: "missing `)` to close function call argument list",
            severity: Severity::Error,
            code: Some("P0032"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(2),
                    end: TokenPosition(7),
                },
                message: "expected `)` before end of file",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(0),
                        end: TokenPosition(0),
                    },
                    message: "function called here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(2),
                        end: TokenPosition(2),
                    },
                    message: "function call argument list starts here",
                },
            ],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0032_03.uc"),
        &ExpectedDiagnostic {
            headline: "missing `)` to close function call argument list",
            severity: Severity::Error,
            code: Some("P0032"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(1),
                    end: TokenPosition(8),
                },
                message: "expected `)` before end of file",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(1),
                    end: TokenPosition(1),
                },
                message: "function call argument list starts here",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_by_code("files/P0032_04.uc", "P0032"),
        &ExpectedDiagnostic {
            headline: "missing `)` to close function call argument list",
            severity: Severity::Error,
            code: Some("P0032"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(6),
                    end: TokenPosition(16),
                },
                message: "expected `)` before end of file",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(3),
                        end: TokenPosition(3),
                    },
                    message: "function called here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(6),
                        end: TokenPosition(6),
                    },
                    message: "function call argument list starts here",
                },
            ],
            help: None,
            notes: &[],
        },
    );
}

#[test]
fn check_p0033_fixtures() {
    let runs = run_fixtures(P0033_FIXTURES);

    assert_eq!(runs.get("files/P0033_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0033_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0033_03.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0033_04.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0033_01.uc"),
        &ExpectedDiagnostic {
            headline: "expected `,` or `)` after argument, found `#`",
            severity: Severity::Error,
            code: Some("P0033"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(7),
                    end: TokenPosition(7),
                },
                message: "unexpected `#`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(5),
                    end: TokenPosition(5),
                },
                message: "argument ends here",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0033_02.uc"),
        &ExpectedDiagnostic {
            headline: "expected `,` or `)` after argument, found `#`",
            severity: Severity::Error,
            code: Some("P0033"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(10),
                    end: TokenPosition(10),
                },
                message: "unexpected `#`",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(3),
                        end: TokenPosition(3),
                    },
                    message: "function called here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(7),
                        end: TokenPosition(7),
                    },
                    message: "argument ends here",
                },
            ],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0033_03.uc"),
        &ExpectedDiagnostic {
            headline: "expected `,` or `)` after argument, found `?`",
            severity: Severity::Error,
            code: Some("P0033"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(9),
                    end: TokenPosition(9),
                },
                message: "unexpected `?`",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(4),
                        end: TokenPosition(4),
                    },
                    message: "function call argument list starts here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(7),
                        end: TokenPosition(7),
                    },
                    message: "argument ends here",
                },
            ],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0033_04.uc"),
        &ExpectedDiagnostic {
            headline: "expected `,` or `)` after argument, found `#`",
            severity: Severity::Error,
            code: Some("P0033"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(12),
                    end: TokenPosition(12),
                },
                message: "unexpected `#`",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(3),
                        end: TokenPosition(3),
                    },
                    message: "function called here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(6),
                        end: TokenPosition(6),
                    },
                    message: "function call argument list starts here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(9),
                        end: TokenPosition(9),
                    },
                    message: "argument ends here",
                },
            ],
            help: None,
            notes: &[],
        },
    );
}