#![allow(clippy::missing_panics_doc, reason = "tests are allowed to panic")]
#![allow(clippy::panic, reason = "tests are allowed panic")]

use std::collections::BTreeMap;

use crate::{
    json,
    test::{self, ExpectFile, Expectation},
    warning,
};

/// Expectations for the unexpected fields reported by `tariff::validate`.
#[derive(Debug, serde::Deserialize)]
pub struct ParseExpect {
    #[serde(default)]
    pub unexpected_fields: Expectation<Vec<json::test::PathGlob>>,
}

/// Expectations for the warnings reported when decoding a tariff from JSON.
#[derive(Debug, serde::Deserialize)]
pub struct FromJsonExpect {
    #[serde(default)]
    warnings: Expectation<BTreeMap<String, Vec<String>>>,
}

/// Assert that the unexpected fields reported by [`tariff::build`](crate::tariff::build)
/// match the expectations of the `ParseExpect` file.
#[track_caller]
pub(crate) fn assert_parse_report(
    mut unexpected_fields: json::PathSet<'_>,
    expect: ExpectFile<ParseExpect>,
) {
    let ExpectFile {
        value,
        expect_file_name,
    } = expect;

    let Some(ParseExpect {
        unexpected_fields: expected,
    }) = value
    else {
        test::expect_no_unexpected_fields(&expect_file_name, &unexpected_fields);
        return;
    };

    test::expect_unexpected_fields(&expect_file_name, &mut unexpected_fields, expected);
}

#[track_caller]
pub(crate) fn assert_from_json_warnings(
    warnings: &warning::Set<super::Warning>,
    expect: ExpectFile<FromJsonExpect>,
) {
    let ExpectFile {
        value,
        expect_file_name,
    } = expect;

    // If there are warnings reported and there is no `expect` file
    // then panic printing the fields of the expect JSON object that would silence these warnings.
    // These can be copied into an `output_lint__*.json` file.
    let Some(expect) = value else {
        assert!(
            warnings.is_empty(),
            "There is no expectation file at `{expect_file_name}`\
            but the tariff has warnings;\n{:#?}",
            warnings.path_debug_map()
        );
        return;
    };

    warning::test::assert_warnings(&expect_file_name, warnings, expect.warnings);
}
