use std::assert_matches;

use crate::{json, lint, tariff, test, warning::Warning as _, Version};

// `NLD` is an `alpha-3` country code, which triggers `CpoCountryCodeShouldBeAlpha2`.
// The `day_of_week` array has `WEDNESDAY` before `MONDAY` (unsorted) and `MONDAY` twice (duplicates),
// so both `Unsorted` and `Duplicates` fire on the same `json::Element`.
const TARIFF: &str = r#"{
    "country_code": "NLD",
    "party_id": "ENE",
    "currency": "EUR",
    "elements": [
        {
            "price_components": [{"type": "ENERGY", "price": 0.25, "step_size": 1}],
            "restrictions": {
                "day_of_week": ["WEDNESDAY", "MONDAY", "MONDAY"]
            }
        }
    ]
}"#;

#[test]
fn should_group_by_elem() {
    test::setup();

    let tariff =
        tariff::build(json::parse_object(TARIFF).unwrap(), Version::V221).ignore_warnings();
    let lint_report = tariff::lint(&tariff);

    let mut iter = lint_report.warnings.iter();

    let (country_code_elem, country_code_warnings) = iter.next().unwrap().to_parts();
    assert_eq!(country_code_elem.path.as_str(), "$.country_code");
    assert_matches!(
        country_code_warnings.as_slice(),
        [lint::tariff::Warning::CpoCountryCodeShouldBeAlpha2]
    );

    let (day_of_week_elem, day_of_week_warnings) = iter.next().unwrap().to_parts();
    assert_eq!(
        day_of_week_elem.path.as_str(),
        "$.elements[0].restrictions.day_of_week"
    );
    // Both Unsorted and Duplicates fire on the same element, so the group has two warnings.
    let ids: Vec<String> = day_of_week_warnings
        .iter()
        .map(|w| w.id().as_str().to_owned())
        .collect();
    assert_eq!(ids, ["unsorted", "duplicates"]);

    assert!(iter.next().is_none(), "No more warning groups expected");

    // The `ElemId` of country_code is lower because it appears earlier in the JSON document.
    // This verifies that Set::iter emits groups in `ElemId` order.
    assert!(country_code_elem.id < day_of_week_elem.id);
}
