#![allow(
    clippy::string_slice,
    clippy::as_conversions,
    reason = "panicking is tests is allowed"
)]

use assert_matches::assert_matches;

use crate::test;

use super::{parse, ErrorKind, SpanBounds, TokenType};

#[test]
fn should_report_trailing_comma() {
    const JSON: &str = r#"{
   "field_a": "one",
   "field_b": "two",
}"#;

    test::setup();

    let err = parse(JSON.into()).unwrap_err();

    assert_matches!(err.kind(), ErrorKind::UnexpectedToken);
    assert_matches!(
        err.token().unwrap().kind,
        TokenType::Comma,
        "We are parsing a comma when we realize that it should not be there"
    );
    assert_eq!(*err.path(), "$.field_b");

    let report = err.into_report(JSON);

    assert_eq!(report.json_context(), r#""two","#);
    let SpanBounds { start, end } = report.span_bounds();
    assert_eq!(*start, (2, 14));
    assert_eq!(*end, (2, 20));
    assert_eq!(report.expand_json_context(), r#"   "field_b": "two","#);
}

#[test]
fn should_report_invalid_json() {
    const JSON: &str = r#"{
"field_"#;

    test::setup();

    let err = parse(JSON.into()).unwrap_err();

    assert_matches!(err.kind(), ErrorKind::UnexpectedToken);
    assert_matches!(
        err.token().unwrap().kind,
        TokenType::Invalid,
        "We are parsing a string not ended be a double quote"
    );
    assert_eq!(*err.path(), "$");

    let report = err.into_report(JSON);

    assert_eq!(report.json_context(), r#""field_"#);
    let SpanBounds { start, end } = report.span_bounds();
    assert_eq!(*start, (1, 0));
    assert_eq!(*end, (1, 7));
    assert_eq!(report.expand_json_context(), r#""field_"#);
}

#[test]
fn should_report_invalid_json_in_some_place() {
    const JSON: &str = r#"{
"field_a": "Barney",
"field_"#;

    test::setup();

    let err = parse(JSON.into()).unwrap_err();

    assert_matches!(err.kind(), ErrorKind::UnexpectedToken);
    assert_matches!(
        err.token().unwrap().kind,
        TokenType::Invalid,
        "We are parsing a string not ended be a double quote"
    );
    assert_eq!(*err.path(), "$");

    let report = err.into_report(JSON);

    assert_eq!(report.json_context(), r#""field_"#);
    let SpanBounds { start, end } = report.span_bounds();
    assert_eq!(*start, (2, 0));
    assert_eq!(*end, (2, 7));
    assert_eq!(report.expand_json_context(), r#""field_"#);
}

#[test]
fn should_report_invalid_json_in_some_place_in_the_middle() {
    const JSON: &str = r#"{
"field_a": "Barney",
"field_b",
"field_c": "Fred" }
"#;

    test::setup();

    let err = parse(JSON.into()).unwrap_err();

    assert_matches!(err.kind(), ErrorKind::UnexpectedToken);
    assert_matches!(
        err.token().unwrap().kind,
        TokenType::Comma,
        "We are parsing a key value pair but the key is followed by comma."
    );
    assert_eq!(*err.path(), "$.field_a");

    let report = err.into_report(JSON);

    assert_eq!(
        report.json_context(),
        r#""Barney",
"field_b","#
    );
    let SpanBounds { start, end } = report.span_bounds();
    assert_eq!(*start, (1, 11));
    assert_eq!(*end, (2, 10));
    assert_eq!(report.expand_json_context(), r#""field_a": "Barney","#);
}

#[test]
fn should_report_missing_comma() {
    const JSON: &str = r#"{
   "field_a": "one"
   "field_b": "two"
}"#;

    test::setup();

    let err = parse(JSON.into()).unwrap_err();

    assert_matches!(err.kind(), ErrorKind::UnexpectedToken);
    assert_matches!(
        err.token().unwrap().kind,
        TokenType::String,
        "We are parsing a String when we realize that there should be a comma"
    );
    assert_eq!(*err.path(), "$.field_a");

    let report = err.into_report(JSON);

    assert_eq!(
        report.json_context(),
        r#""one"
   "field_b""#
    );
    let SpanBounds { start, end } = report.span_bounds();
    assert_eq!(*start, (1, 14));
    assert_eq!(*end, (2, 12));
    assert_eq!(report.expand_json_context(), r#"   "field_a": "one""#);
}
