mod common;
use std::ops::Not;

use serde::{Deserialize, Serialize};

#[test]
fn test_sync_subscription_getters() -> anyhow::Result<()> {
    let ditto = common::get_inactive_builder_ditto(None)?;
    test_sync_subscription_getters_impl(ditto)?;

    let ditto = common::get_inactive_revamp_ditto(None)?;
    test_sync_subscription_getters_impl(ditto)?;

    Ok(())
}

fn test_sync_subscription_getters_impl(
    ditto: dittolive_ditto::prelude::Ditto,
) -> anyhow::Result<()> {
    let sync_subscription = ditto.sync().register_subscription((
        "SELECT * FROM cars WHERE color=:color",
        serde_json::json!({ "color": "red" }),
    ))?;

    assert_eq!(
        sync_subscription.query_string(),
        "SELECT * FROM cars WHERE color=:color"
    );

    let args = sync_subscription.query_arguments().unwrap();
    let args_json = serde_json::to_value(&args)?;
    assert_eq!(args_json, serde_json::json!({ "color": "red" }));

    let args_cbor = sync_subscription.query_arguments_cbor_data().unwrap();
    let args_json_from_cbor: serde_json::Value = serde_cbor::from_slice(&args_cbor)?;
    assert_eq!(args_json_from_cbor, serde_json::json!({ "color": "red" }));

    let args_json_str = sync_subscription.query_arguments_json_str().unwrap();
    let args_json_from_json_str: serde_json::Value = serde_json::from_str(&args_json_str).unwrap();
    assert_eq!(
        args_json_from_json_str,
        serde_json::json!({ "color": "red" })
    );

    assert!(sync_subscription.is_cancelled().not());
    sync_subscription.cancel();
    assert!(sync_subscription.is_cancelled());

    Ok(())
}

#[test]
fn test_sync_subscription_serializable_query_arguments() -> anyhow::Result<()> {
    let ditto = common::get_inactive_revamp_ditto(None)?;

    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
    struct CarArguments {
        color: String,
    }

    let car_arguments = CarArguments {
        color: "red".to_string(),
    };
    let sync_subscription = ditto.sync().register_subscription((
        "SELECT * FROM cars WHERE color=:color",
        car_arguments.clone(),
    ))?;

    let args = sync_subscription.query_arguments().unwrap();
    let args_json = serde_json::to_value(&args)?;
    assert_eq!(args_json, serde_json::json!({ "color": "red" }));

    let args_cbor = sync_subscription.query_arguments_cbor_data().unwrap();
    let car_args_from_cbor: CarArguments = serde_cbor::from_slice(&args_cbor)?;
    assert_eq!(car_args_from_cbor, car_arguments);

    let args_json_str = sync_subscription.query_arguments_json_str().unwrap();
    assert_eq!(args_json_str, serde_json::to_string(&car_arguments)?);

    Ok(())
}

#[test]
fn test_sync_subscription_getters_args_none() -> anyhow::Result<()> {
    let ditto = common::get_inactive_builder_ditto(None)?;
    test_sync_subscription_getters_args_none_impl(ditto)?;

    let ditto = common::get_inactive_revamp_ditto(None)?;
    test_sync_subscription_getters_args_none_impl(ditto)?;

    Ok(())
}

fn test_sync_subscription_getters_args_none_impl(
    ditto: dittolive_ditto::prelude::Ditto,
) -> anyhow::Result<()> {
    let sync_subscription = ditto.sync().register_subscription("SELECT * FROM cars")?;

    assert_eq!(sync_subscription.query_string(), "SELECT * FROM cars");
    let maybe_args = sync_subscription.query_arguments();
    assert!(maybe_args.is_none());

    assert!(sync_subscription.is_cancelled().not());
    sync_subscription.cancel();
    assert!(sync_subscription.is_cancelled());

    Ok(())
}

// Creating identical subscriptions should still yield individual handles
#[test]
fn test_sync_subscription_unique_handles() -> anyhow::Result<()> {
    let ditto = common::get_inactive_builder_ditto(None)?;
    test_sync_subscription_unique_handles_impl(ditto)?;

    let ditto = common::get_inactive_revamp_ditto(None)?;
    test_sync_subscription_unique_handles_impl(ditto)?;

    Ok(())
}

fn test_sync_subscription_unique_handles_impl(
    ditto: dittolive_ditto::prelude::Ditto,
) -> anyhow::Result<()> {
    let sync1 = ditto.sync().register_subscription((
        "SELECT * FROM cars WHERE color=:color",
        serde_json::json!({"color": "red"}),
    ))?;
    assert_eq!(ditto.sync().subscriptions().len(), 1);

    let sync2 = ditto.sync().register_subscription((
        "SELECT * FROM cars WHERE color=:color",
        serde_json::json!({"color": "red"}),
    ))?;
    assert_eq!(ditto.sync().subscriptions().len(), 2);

    sync1.cancel();
    assert_eq!(ditto.sync().subscriptions().len(), 1);

    sync2.cancel();
    assert_eq!(ditto.sync().subscriptions().len(), 0);

    Ok(())
}
