extern crate serde_json;

mod common;

use common::*;
use serde::{Deserialize, Serialize};

#[test]
fn enabled() {
    let ditto = get_builder_ditto(None).unwrap();
    enabled_impl(ditto);

    let ditto = get_revamp_ditto(None).unwrap();
    enabled_impl(ditto);

    let ditto = common::get_revamp_ditto_async_sync(None).unwrap();
    enabled_impl(ditto);
}

fn enabled_impl(ditto: dittolive_ditto::prelude::Ditto) {
    // Default state is true
    assert!(ditto.small_peer_info().is_enabled());
    ditto.small_peer_info().set_enabled(false);
    assert!(ditto.small_peer_info().is_enabled().not());
    ditto.small_peer_info().set_enabled(true);
    assert!(ditto.small_peer_info().is_enabled());
}

#[test]
fn metadata() {
    let ditto = get_builder_ditto(None).unwrap();
    metadata_impl(ditto);

    let ditto = get_revamp_ditto(None).unwrap();
    metadata_impl(ditto);

    let ditto = common::get_revamp_ditto_async_sync(None).unwrap();
    metadata_impl(ditto);
}

fn metadata_impl(ditto: dittolive_ditto::prelude::Ditto) {
    let default_metadata = ditto
        .small_peer_info()
        .metadata::<serde_json::Value>()
        .expect("Getting the default metadata should succeed");
    assert!(default_metadata
        .as_object()
        .expect("Default metadata should be an emtpy object")
        .is_empty());

    #[derive(Serialize, Deserialize, PartialEq, Debug)]
    struct CustomMetadata {
        secret_name: String,
        user: String,
        device_number: usize,
    }

    let secret_name = String::from("Olaf");
    let user = String::from("DefinitelyNotOlaf");
    let device_number = 42;
    let custom_metadata = CustomMetadata {
        secret_name,
        user,
        device_number,
    };

    ditto
        .small_peer_info()
        .set_metadata(&custom_metadata)
        .expect("Setting metadata should work");

    let modified_metadata = ditto
        .small_peer_info()
        .metadata()
        .expect("Getting modified metadata should work");

    assert_eq!(custom_metadata, modified_metadata);
}

#[test]
#[allow(deprecated)]
fn sync_scope() {
    let ditto = get_builder_ditto(None).unwrap();
    sync_scope_impl(ditto);

    let ditto = get_revamp_ditto(None).unwrap();
    sync_scope_impl(ditto);

    let ditto = common::get_revamp_ditto_async_sync(None).unwrap();
    sync_scope_impl(ditto);
}

#[allow(deprecated)]
fn sync_scope_impl(ditto: dittolive_ditto::prelude::Ditto) {
    // Default to Big Peer Only
    assert_eq!(
        ditto.small_peer_info().sync_scope(),
        DittoSmallPeerInfoSyncScope::BigPeerOnly
    );

    ditto
        .small_peer_info()
        .set_sync_scope(DittoSmallPeerInfoSyncScope::LocalPeerOnly);
    assert_eq!(
        ditto.small_peer_info().sync_scope(),
        DittoSmallPeerInfoSyncScope::LocalPeerOnly
    );
}

#[test]
fn metadata_json() {
    let ditto = get_builder_ditto(None).unwrap();
    metadata_json_impl(ditto);

    let ditto = get_revamp_ditto(None).unwrap();
    metadata_json_impl(ditto);

    let ditto = common::get_revamp_ditto_async_sync(None).unwrap();
    metadata_json_impl(ditto);
}

fn metadata_json_impl(ditto: dittolive_ditto::prelude::Ditto) {
    let default_metadata = ditto
        .small_peer_info()
        .metadata_json_string()
        .expect("getting the default metadata should succeed");
    assert_eq!(
        default_metadata, "{}",
        "the default metadata should be an empty JSON object"
    );

    // Setting metadata to an invalid JSON string should fail.
    ditto
        .small_peer_info()
        .set_metadata_json_string("{This is an improper json string}")
        .expect_err("should not be able to set the metadata to an invalid JSON string");

    // JSON with a depth equal to the limit should be okay...
    let nested_64_levels = (1..=64).rev().fold(r#""Hi""#.to_owned(), |acc, next| {
        format!(r#"{{"level{next}":{acc}}}"#)
    });
    ditto
        .small_peer_info()
        .set_metadata_json_string(&nested_64_levels)
        .expect("should be able to set metadata JSON that is nested 64 levels deep");

    // ...but any more levels than the limit should fail.
    let nested_65_levels = format!(r#"{{"level0":{nested_64_levels}}}"#);
    ditto
        .small_peer_info()
        .set_metadata_json_string(&nested_65_levels)
        .expect_err("should not be able to set metadata JSON that is nested 65 levels deep");

    // A failure to set the metadata should not change the previously set metadata.
    let after_failed_set = ditto
        .small_peer_info()
        .metadata_json_string()
        .expect("should be able to get the metadata even after a failed set");
    assert_eq!(
        after_failed_set, nested_64_levels,
        "a failure to set the metadata should not have affected the previously set value"
    );

    let simple_json = "{\"idx\":42}";
    ditto
        .small_peer_info()
        .set_metadata_json_string(simple_json)
        .expect("should be able to set metadata to a simple JSON string");

    let actual = ditto
        .small_peer_info()
        .metadata_json_string()
        .expect("should be able to get the metadata");
    assert_eq!(
        actual, simple_json,
        "getting the metadata JSON string should return the same string that was just set"
    );
}
