use crate::{json_schema, test};

use super::{parse_with_schema, ParseReport};

#[test]
fn should_report_unexpected_fields_for_root_element() {
    const JSON: &str = "null";

    test::setup();

    let schema = json_schema!({
        "id",
        "currency",
    });

    let report = parse_with_schema(JSON.into(), &schema).unwrap();
    let ParseReport {
        element: _,
        unexpected_fields,
    } = report;

    {
        let [field_a] = unexpected_fields.into_inner().try_into().unwrap();
        assert_eq!(*field_a, "$");
    }
}

#[test]
fn should_report_unexpected_fields_in_flat_object() {
    const JSON: &str = r#"{
    "id": "tariff_id",
    "currency": "EUR",
    "name": "Barry",
    "address": "Barrystown"
}"#;

    test::setup();

    let schema = json_schema!({
        "id",
        "currency",
    });

    let report = parse_with_schema(JSON.into(), &schema).unwrap();
    let ParseReport {
        element: _,
        unexpected_fields,
    } = report;

    {
        let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
        assert_eq!(*field_a, "$.name");
        assert_eq!(*field_b, "$.address");
    }
}

#[test]
fn should_report_unexpected_fields_in_nested_object() {
    const JSON: &str = r#"{
    "id": "tariff_id",
    "currency": "EUR",
    "owner": {
        "id": "456856",
        "subscription_id": "tedi4568",
        "name": "Barry",
        "address": "Barrystown"
    }
}"#;

    test::setup();

    let schema = json_schema!({
        "id",
        "currency",
        "owner": {
            "id",
            "subscription_id"
        }
    });

    let report = parse_with_schema(JSON.into(), &schema).unwrap();
    let ParseReport {
        element: _,
        unexpected_fields,
    } = report;

    {
        let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
        assert_eq!(*field_a, "$.owner.name");
        assert_eq!(*field_b, "$.owner.address");
    }
}

#[test]
fn should_parse_nested_object_out_of_schema() {
    const JSON: &str = r#"{
    "id": "tariff_id",
    "owner": {
        "id": "456856",
        "subscription_id": "tedi4568",
        "name": "Barry",
        "address": {
            "city": "Barrystown",
            "street": "Barrysstreet"
        }
    },
    "currency": "EUR",
    "country": "NL"
}"#;

    test::setup();

    let schema = json_schema!({
        "id",
        "currency",
        "owner"
    });

    let report = parse_with_schema(JSON.into(), &schema).unwrap();
    let ParseReport {
        element: _,
        unexpected_fields,
    } = report;

    {
        let [field_a, field_b, field_c, field_d, field_e, field_f] =
            unexpected_fields.into_inner().try_into().unwrap();
        assert_eq!(*field_a, "$.owner.id");
        assert_eq!(*field_b, "$.owner.subscription_id");
        assert_eq!(*field_c, "$.owner.name");
        assert_eq!(*field_d, "$.owner.address.city");
        assert_eq!(*field_e, "$.owner.address.street");
        assert_eq!(*field_f, "$.country");
    }
}

#[test]
fn should_report_unexpected_fields_in_array_with_nested_object() {
    const JSON: &str = r#"{
    "id": "tariff_id",
    "currency": "EUR",
    "elements": [{
        "id": "456856",
        "subscription_id": "tedi4568",
        "name": "Barry",
        "address": "Barrystown"
    }]
}"#;

    test::setup();

    let schema = json_schema!({
        "id",
        "currency",
        "elements": [{
            "id",
            "subscription_id"
        }]
    });

    let report = parse_with_schema(JSON.into(), &schema).unwrap();
    let ParseReport {
        element: _,
        unexpected_fields,
    } = report;

    {
        let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
        assert_eq!(*field_a, "$.elements.0.name");
        assert_eq!(*field_b, "$.elements.0.address");
    }
}

#[test]
fn should_report_unexpected_fields_in_array_of_nested_objects() {
    const JSON: &str = r#"{
    "id": "tariff_id",
    "currency": "EUR",
    "elements": [
        {
            "id": "456856",
            "subscription_id": "tedi4568",
            "name": "Barry",
            "address": "Barrystown"
        },
        {
            "id": "8746we",
            "subscription_id": "dfr345",
            "name": "Gerry",
            "address": "Gerrystown"
        }
    ]
}"#;

    test::setup();

    let schema = json_schema!({
        "id",
        "currency",
        "elements": [{
            "id",
            "subscription_id"
        }]
    });

    let report = parse_with_schema(JSON.into(), &schema).unwrap();
    let ParseReport {
        element: _,
        unexpected_fields,
    } = report;

    {
        let [field_a, field_b, field_c, field_d] =
            unexpected_fields.into_inner().try_into().unwrap();
        assert_eq!(*field_a, "$.elements.0.name");
        assert_eq!(*field_b, "$.elements.0.address");
        assert_eq!(*field_c, "$.elements.1.name");
        assert_eq!(*field_d, "$.elements.1.address");
    }
}

#[test]
fn should_report_unexpected_fields_in_array_of_objects() {
    const JSON: &str = r#"[
    {
        "id": "456856",
        "subscription_id": "tedi4568",
        "name": "Barry",
        "address": "Barrystown"
    },
    {
        "id": "8746we",
        "subscription_id": "dfr345",
        "name": "Gerry",
        "address": "Gerrystown"
    }
]"#;

    test::setup();

    let schema = json_schema!([
        {
            "id",
            "subscription_id"
        }
    ]);

    let report = parse_with_schema(JSON.into(), &schema).unwrap();
    let ParseReport {
        element: _,
        unexpected_fields,
    } = report;

    {
        let [field_a, field_b, field_c, field_d] =
            unexpected_fields.into_inner().try_into().unwrap();
        assert_eq!(*field_a, "$.0.name");
        assert_eq!(*field_b, "$.0.address");
        assert_eq!(*field_c, "$.1.name");
        assert_eq!(*field_d, "$.1.address");
    }
}
