#![allow(non_snake_case)]

use super::*;
use serde_json::json;
use similar_asserts::assert_eq;
use std::convert::TryFrom;
use std::iter::FromIterator;

#[test]
fn Class_parses_from_string() {
    assert_eq!("h-entry".parse(), Ok(Class::Known(KnownClass::Entry)));
    assert_eq!("entry".parse(), Ok(Class::Known(KnownClass::Entry)));
    assert_eq!("h-card".parse(), Ok(Class::Known(KnownClass::Card)));
    assert_eq!("card".parse(), Ok(Class::Known(KnownClass::Card)));
}

#[test]
fn Class_deserializes_from_json() {
    assert_eq!(
        serde_json::from_str::<Class>("\"h-entry\"").map_err(|e| e.to_string()),
        Ok(Class::Known(KnownClass::Entry))
    );
    assert_eq!(
        serde_json::from_str::<Class>("\"entry\"").map_err(|e| e.to_string()),
        Ok(Class::Known(KnownClass::Entry))
    );
}

#[test]
fn class_parses_foreign_type_from_json_string() {
    assert_eq!(
        serde_json::from_str::<Class>("\"h-magic\"").ok(),
        Some(Class::Custom("magic".to_owned()))
    );
}

#[test]
fn class_parses_foreign_type_from_string() {
    assert_eq!("h-magic".parse(), Ok(Class::Custom("magic".to_string())));
}

#[test]
fn item_deserializes_from_json() {
    let the_id = "an-id".to_string();
    let mf2_json = serde_json::json!({
        "properties": {
            "like-of": [
                "http://indieweb.org/like",
                "http://indieweb.org/reply",
            ],
            "comment": [
                "http://indieweb.org/comment",
                "http://indieweb.org/comment",
                "http://indieweb.org/comment",
            ],
            "summary": [
                "Well okay"
            ]
        },
        "id": the_id.clone(),
        "type": ["h-entry"],
    });

    let node = Item {
        id: Some(the_id),
        r#type: vec![Class::Known(KnownClass::Entry)],
        properties: Properties::from_iter(
            [
                (
                    "like-of".to_string(),
                    vec![
                        PropertyValue::Url("http://indieweb.org/like".parse().unwrap()),
                        PropertyValue::Url("http://indieweb.org/reply".parse().unwrap()),
                    ],
                ),
                (
                    "comment".to_string(),
                    vec![
                        PropertyValue::Url("http://indieweb.org/comment".parse().unwrap()),
                        PropertyValue::Url("http://indieweb.org/comment".parse().unwrap()),
                        PropertyValue::Url("http://indieweb.org/comment".parse().unwrap()),
                    ],
                ),
                (
                    "summary".to_string(),
                    vec![PropertyValue::Plain("Well okay".to_string())],
                ),
            ]
            .iter()
            .cloned(),
        ),
        ..Default::default()
    };

    similar_asserts::assert_eq!(serde_json::json!(node), mf2_json);
}

#[test]
fn Item_deserializes_with_from_json_based_on_fixture() {
    let expected_document_result: Result<Document, _> = serde_json::from_value(json!({
        "items": [
        {
            "type": ["h-entry"],
            "properties": {
                "name": ["This should imply a p-name"]
            }
        },
        {
            "type": ["h-entry"],
            "properties": {
                "content": ["This should not imply a p-name since it has an p-* property."]
            }
        },
        {
            "type": ["h-entry"],
            "properties": {
                "content": [{
                    "value": "This should not imply a p-name since it has an e-* property.",
                    "html": "<p>This should not imply a p-name since it has an e-* property.</p>"
                }]
            }
        },
        {
            "type": ["h-entry"],
            "children": [
            {
                "type": ["h-entry"],
                "properties": {
                    "url": ["/foo"]
                }
            }
            ],
            "properties": {
                "like-of": [{
                    "value": "http://microformats.org/",
                    "type": ["h-cite"],
                    "properties": {
                        "name": ["Microformats"],
                        "url": ["http://microformats.org/"]
                    }
                }]
            }
        }
        ],
        "rels": {},
        "rel-urls": {}
    }))
    .map_err(crate::Error::JSON);

    let document = expected_document_result.unwrap();
    assert_eq!(document.items.len(), 4);
    let last_item = document.items.last().cloned();

    assert_ne!(last_item, None);

    let item = last_item.unwrap();
    assert_ne!(item.children, Default::default());
}

#[test]
fn Node_renders_list_of_strings_from_json() {
    let json_string = r#"[
            "microformats",
            "rust-lang"
        ]"#;
    let json_val_result: Result<NodeList, serde_json::Error> = serde_json::from_str(json_string);
    let expected_node: NodeList = vec![
        PropertyValue::Plain("microformats".to_string()),
        PropertyValue::Plain("rust-lang".to_string()),
    ];
    assert!(json_val_result.is_ok());
    let json_val = json_val_result.unwrap();
    assert_eq!(json_val, expected_node);
}

#[test]
fn Document_empty_serializes_from_json() {
    let mut document = Document::new(None);
    let item = Item::new(vec![Class::Known(KnownClass::Entry)]);
    document.items.push(item);

    assert_eq!(
        json!({
            "items": [
            {
                "type": ["h-entry"],
                "properties": {},
            }
            ],
            "rels": {},
            "rel-urls": {}
        }),
        json!(document)
    );
}

#[test]
fn Item_properties_add() {
    let mut item = Item::default();
    item.append_property("magic", PropertyValue::Plain("add".to_string()));
    item.append_property("magic", PropertyValue::Plain("add".to_string()));

    assert_eq!(item.properties.get("magic").map(|v| v.len()), Some(2));
}

#[test]
fn PropertyValue_kinds() {
    let u: Url = "https://indieweb.org".parse().unwrap();

    assert_eq!(
        serde_json::from_str(&format!("{:?}", u.to_string())).map_err(crate::Error::JSON),
        Ok(PropertyValue::Url(u))
    );

    assert_eq!(
        serde_json::from_str(
            r#"
          {
            "alt": "company logos",
            "value": "http://example.com/images/logo.gif"
          }
        "#
        )
        .map_err(crate::Error::JSON),
        Ok(PropertyValue::Image(Image {
            value: "http://example.com/images/logo.gif".parse().unwrap(),
            alt: Some("company logos".to_string()),
        }))
    );
}

#[test]
fn try_from_json_for_item() {
    assert_eq!(
        Ok(Item::new(vec![KnownClass::Entry.into()])),
        Item::try_from(json!({"type": ["h-entry"], "properties": {}}))
    );
}
