mod common;

#[cfg(test)]
mod snippets {
    use std::sync::Arc;

    use dittolive_ditto::error::DittoError;

    use super::common::*;

    #[ignore]
    #[test]
    fn shared_key() -> Result<(), DittoError> {
        let license_token = std::env::var("DITTO_LICENSE").expect("No License Env Var provided");
        //@ditto/snippet-start shared-key
        // This is just an example. You should use OpenSSL to generate a unique shared key for every
        // application.
        let p256_der_b64: &str = "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgFUUrOkOH52QN+Rr6uDSDsk4hUTcD1eW4mT0UnGGptFehRANCAATJ3fG8TVLQcDwUV18BJJI8efK0hQAjzB3VJeYOVbfOlqnfukVId0V25r/abxwjD3HfHuPsCGEiefzzmkMbjPo9";
        let database_id = DatabaseId::from_env("app")?;
        let ditto = Ditto::builder()
            .with_root(Arc::new(PersistentRoot::from_current_exe()?))
            .with_identity(|ditto_root| {
                identity::SharedKey::new(ditto_root, database_id, p256_der_b64)
            })?
            .build()?;
        let res = ditto.set_offline_only_license_token(&license_token);
        ditto.sync().start()?;
        //@ditto/snippet-end
        assert!(res.is_ok());
        ditto.sync().stop();
        Ok(())
    }

    #[ignore]
    #[test]
    fn online_playground() -> Result<(), DittoError> {
        //@ditto/snippet-start online-playground
        let ditto = Ditto::builder()
            // creates a `ditto_data` folder in the directory containing the executing process
            .with_root(Arc::new(PersistentRoot::from_current_exe()?))
            .with_identity(|ditto_root| {
                // Provided as an env var, may also be provided as hardcoded string
                let database_id = DatabaseId::from_env("00000000-0000-4000-0000-000000000000")?;
                let shared_token = std::env::var("REPLACE_ME_WITH_A_SHARED_TOKEN").unwrap();
                let enable_cloud_sync = true;
                let custom_auth_url = None;
                OnlinePlayground::new(
                    ditto_root,
                    database_id,
                    shared_token,
                    enable_cloud_sync,
                    custom_auth_url,
                )
            })?
            //@ditto/snippet-include-next-line
            //.with_minimum_log_level(LogLevel::Debug)
            .build()?;

        ditto.sync().start()?;
        //@ditto/snippet-end
        ditto.sync().stop();
        Ok(())
    }

    #[ignore]
    #[test]
    fn offline_playground() -> Result<(), DittoError> {
        let license_token = std::env::var("DITTO_LICENSE").expect("No License Env Var provided");
        //@ditto/snippet-start offline-playground
        let ditto = Ditto::builder()
            // creates a `ditto_data` folder in the directory containing the executing process
            .with_root(Arc::new(PersistentRoot::from_current_exe()?))
            .with_identity(|ditto_root| {
                // Provided as an env var, may also be provided as hardcoded string
                let database_id = DatabaseId::from_env("00000000-0000-4000-0000-000000000000")?;
                OfflinePlayground::new(ditto_root, database_id)
            })?
            .build()?;

        ditto.sync().start()?;
        let res = ditto.set_offline_only_license_token(&license_token);
        //@ditto/snippet-end
        assert!(res.is_ok());
        ditto.sync().stop();
        Ok(())
    }

    #[test]
    fn network_remote_ditto() -> Result<(), DittoError> {
        let ditto = get_builder_ditto(None).unwrap();
        network_remote_ditto_impl(ditto)?;

        let ditto = get_revamp_ditto(None).unwrap();
        network_remote_ditto_impl(ditto)?;

        Ok(())
    }

    fn network_remote_ditto_impl(ditto: dittolive_ditto::prelude::Ditto) -> Result<(), DittoError> {
        //@ditto/snippet-start network-remote-ditto
        let mut config = TransportConfig::new(); // empty

        config
            .connect
            .tcp_servers
            .insert("135.1.5.5:12345".to_string()); // Custom TCP Listener
        config
            .connect
            .tcp_servers
            .insert("185.1.5.5:12345".to_string()); // Custom TCP Listener
        config
            .connect
            .websocket_urls
            .insert("wss://example.com".to_string()); // Custom WS endpoint

        ditto.set_transport_config(config);
        ditto.sync().start()?;
        //@ditto/snippet-end
        ditto.sync().stop();
        Ok(())
    }

    #[test]
    fn network_listen() -> Result<(), DittoError> {
        let ditto = get_builder_ditto(None).unwrap();
        network_listen_impl(ditto)?;

        let ditto = get_revamp_ditto(None).unwrap();
        network_listen_impl(ditto)?;

        Ok(())
    }

    fn network_listen_impl(ditto: dittolive_ditto::prelude::Ditto) -> Result<(), DittoError> {
        //@ditto/snippet-start network-listen
        let mut config = TransportConfig::new(); // empty

        config.listen.tcp.enabled = true;
        config.listen.tcp.interface_ip = "0.0.0.0".to_string();
        config.listen.tcp.port = 4000;
        config.listen.http.enabled = false;

        ditto.set_transport_config(config);
        ditto.sync().start()?;
        //@ditto/snippet-end
        ditto.sync().stop();
        Ok(())
    }

    #[test]
    fn sync_groups() -> Result<(), DittoError> {
        let ditto = get_builder_ditto(None).unwrap();
        sync_groups_impl(ditto)?;

        let ditto = get_revamp_ditto(None).unwrap();
        sync_groups_impl(ditto)?;

        Ok(())
    }

    fn sync_groups_impl(ditto: dittolive_ditto::prelude::Ditto) -> Result<(), DittoError> {
        //@ditto/snippet-start transport-sync-groups
        let mut config = TransportConfig::new(); // empty

        config.enable_all_peer_to_peer();
        config.global.sync_group = 1234;

        ditto.set_transport_config(config);
        ditto.sync().start()?;
        //@ditto/snippet-end
        ditto.sync().stop();
        Ok(())
    }

    #[test]
    fn network_three() -> Result<(), DittoError> {
        let ditto = get_builder_ditto(None).unwrap();
        network_three_impl(ditto)?;

        let ditto = get_revamp_ditto(None).unwrap();
        network_three_impl(ditto)?;

        Ok(())
    }

    fn network_three_impl(ditto: dittolive_ditto::prelude::Ditto) -> Result<(), DittoError> {
        //@ditto/snippet-start network-multiple-transports
        let mut config = TransportConfig::new(); // empty

        // 1. Enable auto-discovery of peer to peer connections
        config.enable_all_peer_to_peer(); // Auto-connect via lan and bluetooth

        // 2. Configure TCP Listener
        config.listen.tcp.enabled = true;
        config.listen.tcp.interface_ip = "0.0.0.0".to_string();
        config.listen.tcp.port = 4000;
        config.listen.http.enabled = false;

        // 3. Configure explicit, hard coded connections
        config
            .connect
            .tcp_servers
            .insert("135.1.5.5:12345".to_string()); // Custom TCP Listener
        config
            .connect
            .websocket_urls
            .insert("wss://example.com".to_string()); // Custom WS endpoint

        ditto.set_transport_config(config);
        ditto.sync().start()?;
        //@ditto/snippet-end
        ditto.sync().stop();
        Ok(())
    }

    #[tokio::test]
    async fn dittolive_homepage() -> anyhow::Result<()> {
        let ditto = get_builder_ditto(None)?;
        dittolive_homepage_impl(ditto).await?;

        let ditto = get_revamp_ditto(None)?;
        dittolive_homepage_impl(ditto).await?;

        Ok(())
    }

    async fn dittolive_homepage_impl(ditto: dittolive_ditto::prelude::Ditto) -> anyhow::Result<()> {
        use dittolive_ditto::dql::QueryResult;

        //@ditto/snippet-start dittolive-homepage
        //@ditto/snippet-include-next-line
        // let ditto = Ditto::new(...);
        ditto.sync().start()?;

        let results = ditto
            .store()
            .execute("INSERT INTO cars DOCUMENTS ({ 'color': 'blue' })")
            .await?;

        let observer = ditto.store().register_observer(
            "SELECT * FROM cars",
            move |_result: QueryResult| {
                // handle changes
            },
        )?;

        let subscription = ditto.sync().register_subscription("SELECT * FROM cars")?;

        //@ditto/snippet-end
        drop(subscription);
        drop(observer);
        drop(results);

        Ok(())
    }
}
