#![allow(deprecated)]
mod common;

use std::sync::{
    atomic::{AtomicBool, Ordering},
    Arc,
};

// By importing every single public item in the crate, we can ensure
// that no mucking around or cleaning up is accidentally causing breakages
// to import paths
//
// WARNING: if deprecating something new, move it to the second `use` in this function, which has
// `#[expect(deprecated)]`. Do not put `#[allow(deprecated)]` on this test
#[test]
#[allow(unused_imports)]
fn ensure_v4_no_broken_imports() {
    // This block shows where the proper public home for each item is
    #[rustfmt::skip]
    use dittolive_ditto::{
        disk_usage::{
            self,
            DiskUsage,
            DiskUsageCallback,
            DiskUsageChild,
            DiskUsageObserverCtx,
            DiskUsageObserverHandle,
            FileSystemType,
        },
        dql::{
            self,
            QueryResult,
            QueryResultItem,
        },
        error::{
            self,
            CoreApiErrorKind,
            DittoError,
            ErrorKind,
            Result,
        },
        fs::{
            self,
            DittoRoot,
            PersistentRoot,
            TempRoot,
        },
        identity::{
            self,
            AuthenticationClientFeedback,
            DittoAuthenticator,
            DittoAuthenticationEventHandler,
            Manual,
            OfflinePlayground,
            OnlinePlayground,
            OnlineWithAuthentication,
            SharedKey,
        },
        logger::{
            self,
            DittoLogger,
        },
        prelude::{
            self,
            identity as _,
            AppId as _,
            BoxedDitto,
            BoxedDocument,
            CLogLevel,
            COrderByParam as _,
            CborValue,
            CborValueGetters,

            ConnectionRequest as _,
            ConnectionRequestAuthorization as _,
            DiskUsage as _,
            Ditto as _,
            DittoAttachment as _,
            DittoAttachmentFetchEvent as _,
            DittoAttachmentFetcher as _,
            DittoAttachmentToken as _,
            DittoAuthenticationEventHandler as _,
            DittoAuthenticator as _,
            DittoError as _,
            DittoLogger as _,
            DittoRoot as _,
            HttpListenConfig as _,
            Identity,

            LogLevel as _,
            Manual as _,
            OfflinePlayground as _,
            OnlinePlayground as _,
            OnlineWithAuthentication as _,

            PersistentRoot as _,
            Presence as _,
            PresenceObserver as _,
            QuerySortDirection as _,
            SharedKey as _,

            Store as _,
            StringPrimitiveFormat,

            TcpListenConfig as _,
            TempRoot as _,
            TransportConfig as _,
            WriteStrategy as _,
        },
        presence::{
            self,
            Connection,
            ConnectionRequest,
            ConnectionRequestAuthorization,
            ConnectionType,
            JsonObject,
            Peer,
            Presence,
            PresenceGraph,
            PresenceObserver,
            PresenceOs,
        },
        small_peer_info::{
            self,
            SmallPeerInfo,
        },
        store::{
            self,
            attachment::{
                self,
                DittoAttachment,
                DittoAttachmentFetchEvent,
                DittoAttachmentFetcher,
                DittoAttachmentToken,
                DittoAttachmentTokenLike,
                FetcherVersion,
            },

            ChangeHandler,
            ChangeHandlerWithSignalNext,
            SignalNext,
            SortDirection,
            Store,
            StoreObserver,
            WriteStrategy,
        },
        sync::{
            self,
            Sync,
            SyncSubscription,
        },
        transport::{
            self,
            BluetoothLEConfig,
            Connect,
            Global,
            HttpListenConfig,
            LanConfig,
            Listen,
            PeerToPeer,
            TcpListenConfig,
            TransportConfig,
        },
        AppId,
        Ditto,
        LogLevel,
    };
    // TODO(v5): Remove deprecated items
    // This block is made of items that are deprecated or exported from a
    // location that is deprecated. This test ensures that those items are still
    // reachable (so as to not break callers), but at v5 we should remove these
    // re-exports
    #[rustfmt::skip]
    #[expect(deprecated)]
    use dittolive_ditto::{
        small_peer_info::DittoSmallPeerInfoSyncScope as _,
        DittoBuilder,
        auth::{
            self,
            authenticator::{
                self,
                AuthenticationClientFeedback as _,
                DittoAuthenticator as _,
            },
            login_provider::{
                self,
                DittoAuthenticationEventHandler as _,
            },
            validity_listener::{
                self,
                ValidityListener as _,
            },
            AuthenticationClientFeedback as _,
            DittoAuthenticator as _,
            DittoAuthenticationEventHandler as _,
            ValidityListener as _,
        },
        ditto::{
            self,
            builder::{
                self,
                DittoBuilder as _,
            },
            AppId as _,
            Ditto as _,
            LogLevel as _,
            SiteId as _,
        },
        dql::{
            ChangeHandler as _,
            ChangeHandlerWithSignalNext as _,
            SignalNext as _,
            StoreObserver as _,
            SyncSubscription as _,
            Query as _,
            QueryArguments as _,
        },
        identity::OnlinePlaygroundV2 as _,
        observer::{
            self,
            Observer,
        },
        prelude::{
            DittoBuilder as _,
            Observer as _,
            PeersObserver as _,
            SiteId as _,
            TimeSeries as _,
        },
        store::{
            attachment::{
                fetch_event::{
                    self,
                    DittoAttachmentFetchEvent as _,
                },
                fetcher::{
                    self,
                    DittoAttachmentFetcher as _,
                    FetcherVersion::{
                        self as _,
                        V1,
                        V2,
                    },
                },
                token::{
                    self,
                    DittoAttachmentToken as _,
                    DittoAttachmentTokenLike as _,
                },
            },
            batch::{
                self,
                Action as _,
                DocChangeKind as _,
                ScopedStore as _,
                WriteTransactionResult as _,
            },
            collection::{
                self,
                document::{
                    self,
                    DittoDocument as _,
                    DittoMutDocument as _,
                },
                document_id::{
                    self,
                    DocumentId as _,
                },
                pending_cursor_operation::{
                    self,
                    PendingCursorOperation as _,
                },
                pending_id_specific_operation::{
                    self,
                    PendingIdSpecificOperation as _,
                },
                type_traits::{
                    self,
                    MutableValue as _,
                },
                Collection as _,
                DittoDocument as _,
                DittoMutDocument as _,
                DocumentId as _,
                MutableValue as _,
                PendingCursorOperation as _,
                PendingIdSpecificOperation as _,
            },
            collections::{
                self,
                event::{
                    self,
                    CollectionsEvent as _,
                },
                pending_collections_operation::{
                    self,
                    CollectionsEventHandler as _,
                    PendingCollectionsOperation as _,
                },
                CollectionsEvent as _,
                CollectionsEventHandler as _,
                PendingCollectionsOperation as _,
            },
            live_query::{
                self,
                EventHandler as _,
                LiveQuery as _,
                LiveQueryEvent as _,
                LiveQueryMove as _,
                SingleDocumentEventHandler as _,
                SingleDocumentLiveQueryEvent as _,
            },
            dql::{
                self as _,
                ChangeHandler as _,
                ChangeHandlerWithSignalNext as _,
                Query as _,
                QueryArguments as _,
                QueryResult as _,
                QueryResultItem as _,
                SignalNext as _,
                StoreObserver as _,
                SyncSubscription as _,
            },
            update::{
                self,
                UpdateOp as _,
                UpdateResult as _,
            },
            timeseries::{
                self,
                DittoTimestamp,
                TimeSeries,
            },
        },
        subscription::{
            self,
            Subscription as _,
        },
        types::{
            self,
            DittoCounter as _,
            DittoMutableCounter as _,
            DittoMutableRegister as _,
            DittoRegister as _,
        },
        transport::TransportSync,
        prelude::{
            DittoSmallPeerInfoSyncScope as _,
            Collection as _,
            CollectionsEvent as _,
            CollectionsEventHandler as _,
            DittoCounter as _,
            DittoDocument as _,
            DittoMutDocument as _,
            DittoRegister as _,
            DocumentId as _,
            EventHandler as _,
            LiveQuery as _,
            LiveQueryEvent as _,
            LiveQueryMove as _,
            PendingCollectionsOperation as _,
            PendingCursorOperation as _,
            SingleDocumentEventHandler as _,
            SingleDocumentLiveQueryEvent as _,
            Subscription as _,
        },
    };
}

#[test]
fn ensure_activated_before_starting_sync() {
    let ditto = common::get_inactive_builder_ditto(None).unwrap();
    ensure_activated_before_starting_sync_impl(ditto);

    let ditto = common::get_inactive_revamp_ditto(None).unwrap();
    ensure_activated_before_starting_sync_impl(ditto);
}

fn ensure_activated_before_starting_sync_impl(ditto: dittolive_ditto::prelude::Ditto) {
    let res = ditto.sync().start();
    assert!(res.is_err());
    let err = res.err().unwrap();
    assert_eq!(err.to_string(), "Sync could not be started because Ditto has not yet been activated. This can be achieved with a successful call to `set_license_token`. If you need to obtain a license token then please visit https://portal.ditto.live.");
}

#[test]
fn ensure_online_is_activated_before_starting_sync() {
    let ditto = common::get_online_ditto().unwrap();
    assert!(ditto.is_activated());
    // We go through this extra `Result`-wrapping closure layer to get
    // the corresponding API doc Rust snippet to feature a more idiomatic `?`
    let res = (|| {
        //@ditto/snippet-start sync-basic
        ditto.sync().start()?;
        //@ditto/snippet-end
        Ok::<(), ::dittolive_ditto::error::DittoError>(())
    })();
    assert!(res.is_ok());
}

/// A basic smoke test which tests that the FFI bindings are wired up
/// correctly and nothing horrible happens.
#[test]
fn test_garbage_collection() {
    let ditto = common::get_builder_ditto(None).unwrap();
    test_garbage_collection_impl(ditto);

    let ditto = common::get_revamp_ditto(None).unwrap();
    test_garbage_collection_impl(ditto);

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

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

fn test_garbage_collection_impl(ditto: dittolive_ditto::prelude::Ditto) {
    ditto.run_garbage_collection();
}

/// Test that setting a device name is working correctly
#[test]
fn test_set_device_name() {
    let ditto = common::get_builder_ditto(None).unwrap();
    test_set_device_name_impl(ditto);

    let ditto = common::get_revamp_ditto(None).unwrap();
    test_set_device_name_impl(ditto);

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

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

fn test_set_device_name_impl(ditto: dittolive_ditto::prelude::Ditto) {
    ditto.set_device_name("my_custom_device_name");

    let finished = Arc::new(AtomicBool::new(false));
    let finished_1 = Arc::clone(&finished);

    let _presence_observer = ditto.presence().register_observer(move |presence| {
        let local_peer = &presence.local_peer;

        assert_eq!(&local_peer.device_name, "my_custom_device_name");
        finished_1.store(true, Ordering::SeqCst);
    });

    while !finished.load(Ordering::SeqCst) {
        std::thread::yield_now();
    }
}

/// Test that monitoring disk usage works
#[test]
fn test_observe_disk_usage() {
    let ditto = common::get_builder_ditto(None).unwrap();
    test_observe_disk_usage_impl(ditto);

    let ditto = common::get_revamp_ditto(None).unwrap();
    test_observe_disk_usage_impl(ditto);

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

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

fn test_observe_disk_usage_impl(ditto: dittolive_ditto::prelude::Ditto) {
    let finished = Arc::new(AtomicBool::new(false));
    let finished_1 = Arc::clone(&finished);

    let _observer = ditto.disk_usage().observe(move |_disk_usage_tree| {
        finished_1.store(true, Ordering::SeqCst);
    });
    let mut data_path = ditto.absolute_persistence_directory();
    data_path.push("random_file_name");
    std::fs::File::create(data_path).unwrap();

    while !finished.load(Ordering::SeqCst) {
        std::thread::yield_now();
    }
}

#[test]
fn test_logout() {
    let ditto = common::get_online_ditto().unwrap();
    ditto.auth().unwrap().logout(|_| {}).unwrap();
}
