use super::*;

#[test]
fn parse_safety_attr() {
    let parse_attr = |s: &str| parse_str::<SafetyAttr>(s);

    // empty SP
    let _: SafetyAttr = parse_attr("#[safety {}]").unwrap();

    // simplest SP
    let attr = parse_attr("#[safety { SP }]").unwrap();
    let sp = attr.args.property_reason().next().unwrap().0;
    assert_eq!(sp.tag.name_type(), ("SP", None));

    // SP with path prefix and arguments
    let attr = parse_attr("#[safety { hazard.Alias(p, q) }]").unwrap();
    let sp = attr.args.property_reason().next().unwrap().0;
    assert_eq!(sp.tag.name_type(), ("Alias", Some(TagType::Hazard)));
    let args: Vec<_> = sp
        .args
        .iter()
        .map(|arg| {
            if let Expr::Path(path) = arg {
                path.path.get_ident().unwrap().to_string()
            } else {
                unreachable!()
            }
        })
        .collect();
    assert_eq!(args, ["p", "q"]);
}

fn parse_args(s: &str) -> syn::Result<SafetyAttrArgs> {
    parse_str::<SafetyAttrArgs>(s)
}

#[test]
fn parse_safety_args() {
    // vanilla SP
    _ = parse_args("SP").unwrap();
    _ = parse_args("SP1, SP2").unwrap();
    _ = parse_args("SP1; SP2").unwrap();

    // SP with reason
    _ = parse_args(r#" SP1: "reason" "#).unwrap();
    _ = parse_args(r#" SP1: "reason"; SP2: "reason" "#).unwrap();
    _ = parse_args(r#" SP1: "reason", SP2: "reason" "#).unwrap_err();

    // grouped SPs
    _ = parse_args(r#" SP1, SP2: "reason" "#).unwrap();
    _ = parse_args(r#" SP1, SP2: "reason"; SP3 "#).unwrap();
    _ = parse_args(r#" SP3; SP1, SP2: "reason" "#).unwrap();
    _ = parse_args(r#" SP3, SP4; SP1, SP2: "reason" "#).unwrap();
    _ = parse_args(r#" SP3; SP1, SP2: "reason"; SP4 "#).unwrap();

    // trailing punct
    _ = parse_args(r#" SP1, SP2: "reason"; SP3; "#).unwrap();
    _ = parse_args(r#" SP1, SP2: "reason"; SP3, "#).unwrap();

    // arguments in single SP
    _ = parse_args(r#" SP1(a) "#).unwrap();
    _ = parse_args(r#" SP1(a, b) "#).unwrap();
    _ = parse_args(r#" SP1(a, b, call()) "#).unwrap();

    // arguments with other SPs
    _ = parse_args(r#" SP1(a), SP2: "reason"; SP3 "#).unwrap();
    _ = parse_args(r#" SP(a, b): "reason"; SP1, SP2: "reason"; SP3, SP4 "#).unwrap();
}

#[test]
fn parse_safety_complex_args() {
    // SP path prefix
    _ = parse_args(r#" hazard.Alias(p, q) "#).unwrap();

    // complex expressions in arguments
    _ = parse_args(r#" hazard.Alias(A {a: self.a}, a::b(c![])) : "" "#).unwrap();
}

#[test]
fn new_single_sp() {
    let name = "Tag";
    let sp = PropertiesAndReason::new_single_sp(name);
    assert_eq!(sp.tags.len(), 1, "{sp:#?} must have single property");
    assert!(sp.tags[0].args.is_empty(), "{sp:#?} must have no arguments");
    assert!(sp.tags[0].tag.typ.is_none());
    assert_eq!(&*sp.tags[0].tag.name, name);
}

#[test]
fn parse_sp_str() {
    let any = "any(Tag1, Tag2)";
    let tag1 = "Tag1";
    let tag2 = "Tag2";
    let sp = PropertiesAndReason::parse_sp_str(any).unwrap();
    assert_eq!(sp.tags.len(), 1, "{sp:#?} must have single property");
    assert!(sp.tags[0].tag.typ.is_none());
    assert_eq!(&*sp.tags[0].tag.name, ANY);
    assert_eq!(&*sp.tags[0].args_as_string(), [tag1, tag2]);

    let args = sp.tags[0]
        .args_in_any_tag()
        .unwrap_or_else(|| panic!("{sp:#?} must be any `any` SP with two args"));
    assert_eq!(&*args[0].tags[0].tag.name, tag1);
    assert_eq!(&*args[1].tags[0].tag.name, tag2);
}
