use std::assert_matches;

use crate::{
    cdr,
    json::{self, FromJson as _},
    price::{self, v221, Warning},
    test::{self, datetime_from_str},
};

#[test]
fn should_pass_parse_validation() {
    test::setup();
    let json = cdr_json("2022-01-13T16:00:00Z", "2022-01-13T19:12:00Z");
    let doc = json::parse_object(&json).unwrap();
    let cdr = cdr::build(doc, crate::Version::V221);
    test::assert_no_unexpected_fields(&cdr);

    let (_cdr, warnings) = v221::Cdr::from_json(cdr.as_element()).unwrap().into_parts();
    assert!(warnings.is_empty());
}

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

    let json = cdr_json("2022-02-13T16:00:00Z", "2022-02-13T19:12:00Z");
    let doc = json::parse_object(&json).unwrap();
    let cdr = cdr::build(doc, crate::Version::V221);
    test::assert_no_unexpected_fields(&cdr);

    let (_cdr, warnings) = v221::Cdr::from_json(cdr.as_element()).unwrap().into_parts();
    let [warning] = warnings
        .into_path_as_str_map()
        .remove("$")
        .unwrap()
        .try_into()
        .unwrap();
    let Warning::PeriodsOutsideStartEndDateTime {
        cdr_range,
        period_range,
    } = warning
    else {
        panic!("expected PeriodsOutsideStartEndDateTime, got {warning:?}");
    };

    {
        assert_eq!(cdr_range.start, datetime_from_str("2022-02-13T16:00:00Z"));
        assert_eq!(cdr_range.end, datetime_from_str("2022-02-13T19:12:00Z"));
    }
    {
        let price::PeriodRange::Many(period_range) = period_range else {
            panic!("expected PeriodRange::Many, got {period_range:?}");
        };

        assert_eq!(
            period_range.start,
            datetime_from_str("2022-01-13T16:00:00Z")
        );
        assert_eq!(period_range.end, datetime_from_str("2022-01-13T18:30:00Z"));
    }
}

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

    let json = serde_json::json!({
        "country_code": "NL",
        "party_id": "ENE",
        "start_date_time": "2022-01-13T16:00:00Z",
        "end_date_time": "2022-01-13T19:12:00Z",
        "currency": "EUR",
        "tariffs": [],
        "cdr_location": { "country": "NLD" },
        "charging_periods": [
            {
                "start_date_time": "2022-01-13T16:00:00Z",
                "dimensions": [{ "type": "TIME" }]
            }
        ],
        "total_cost": { "excl_vat": 4.00, "incl_vat": 4.40 },
        "total_time": 3.2,
        "total_energy": 0,
        "last_updated": "2022-01-13T00:00:00Z"
    })
    .to_string();

    let doc = json::parse_object(&json).unwrap();
    let cdr = cdr::build(doc, crate::Version::V221);
    test::assert_no_unexpected_fields(&cdr);

    let err = v221::Cdr::from_json(cdr.as_element()).unwrap_err();
    let (error, _warnings) = err.into_parts();
    let (warning, _element) = error.into_parts();
    assert_matches!(warning, Warning::FieldRequired { field_name } if field_name == "volume");
}

fn cdr_json(start_date_time: &str, end_date_time: &str) -> String {
    let value = serde_json::json!({
        "country_code": "NL",
        "party_id": "ENE",
        "start_date_time": start_date_time,
        "end_date_time": end_date_time,
        "currency": "EUR",
        "tariffs": [],
        "cdr_location": {
            "country": "NLD"
        },
        "charging_periods": [
            {
                "start_date_time": "2022-01-13T16:00:00Z",
                "dimensions": [
                    {
                        "type": "TIME",
                        "volume": 2.5
                    }
                ]
            },
            {
                "start_date_time": "2022-01-13T18:30:00Z",
                "dimensions": [
                    {
                        "type": "PARKING_TIME",
                        "volume": 0.7
                    }
                ]
            }
        ],
        "total_cost": {
            "excl_vat": 11.25,
            "incl_vat": 12.75
        },
        "total_time_cost": {
            "excl_vat": 7.5,
            "incl_vat": 8.25
        },
        "total_parking_time": 0.7,
        "total_parking_cost": {
            "excl_vat": 3.75,
            "incl_vat": 4.5
        },
        "total_time": 3.2,
        "total_energy": 0,
        "last_updated": "2022-01-13T00:00:00Z"
    });

    value.to_string()
}
