//! C headers written in Rust.

#[rustfmt::skip]
use {
    super::*,
    ::safer_ffi::{
        prelude::{derive_ReprC, VirtualPtr},
    },
};

#[apply(extern_type_polyfill!)]
unsafe extern "C" {
    // we need to let the build.rs script control the dylib vs. static choice
    pub type Ditto: Send + Sync;
    #[drop]
    pub fn ditto_free(ditto: c::Box<Ditto>);

    pub fn ditto_shutdown(ditto: &'_ Ditto);

    pub fn ditto_init_sdk_version(
        platform: Platform,
        language: Language,
        sdk_semver: char_p::Ref<'_>,
    );

    pub type IdentityConfig: Send + Sync;

    pub fn ditto_make(
        working_dir: char_p::Ref<'_>,
        identity_config: c::Box<IdentityConfig>,
        history_tracking: HistoryTracking,
    ) -> c::Box<Ditto>;

    pub fn ditto_auth_client_activate(ditto: &'_ Ditto);

    pub fn ditto_logger_init();
    pub fn ditto_logger_enabled(enabled: bool);
    pub fn ditto_logger_enabled_get() -> bool;
    pub fn ditto_logger_emoji_headings_enabled(enabled: bool);
    pub fn ditto_logger_emoji_headings_enabled_get() -> bool;
    pub fn ditto_logger_minimum_log_level_get() -> CLogLevel;
    pub fn ditto_logger_minimum_log_level(log_level: CLogLevel);

    pub fn ditto_start_tcp_server(ditto: &'_ Ditto, bind: Option<char_p::Ref<'_>>) -> c_int;
    pub fn ditto_stop_tcp_server(ditto: &'_ Ditto);

    pub fn ditto_start_http_server(
        ditto: &'_ Ditto,
        bind: Option<char_p::Ref<'_>>,
        static_path: Option<char_p::Ref<'_>>,
        enable_websocket: WebSocketMode,
        tls_cert_path: Option<char_p::Ref<'_>>,
        tls_key_path: Option<char_p::Ref<'_>>,
    ) -> c_int;
    pub fn ditto_stop_http_server(ditto: &'_ Ditto);

    pub fn ditto_add_static_tcp_client(
        ditto: &Ditto,
        address: char_p::Ref<'_>,
    ) -> c::Box<StaticTcpClientPlatformHandle>;

    pub fn ditto_add_websocket_client(
        ditto: &'_ Ditto,
        address: char_p::Ref<'_>,
        routing_hint: c_uint,
    ) -> c::Box<WebsocketClientPlatformHandle>;

    pub fn ditto_add_internal_mdns_client_transport(
        ditto: &'_ Ditto,
    ) -> c::Box<MdnsClientPlatformHandle>;
    pub fn ditto_add_internal_mdns_server_transport(
        ditto: &'_ Ditto,
    ) -> c::Box<MdnsServerPlatformHandle>;

    pub fn ditto_add_internal_ble_client_transport(
        ditto: &'_ Ditto,
    ) -> c::Box<BleClientPlatformHandle>;
    pub fn ditto_add_internal_ble_server_transport(
        ditto: &'_ Ditto,
    ) -> c::Box<BleServerPlatformHandle>;

    pub fn ditto_add_multicast_transport(ditto: &'_ Ditto);
    pub fn ditto_remove_multicast_transport(ditto: &'_ Ditto);

    pub fn ditto_small_peer_info_set_transport_config_data(
        ditto: &'_ Ditto,
        cbor: c_slice::Ref<'_, u8>,
    );

    pub fn ditto_get_sdk_version() -> char_p::Box;

    pub fn ditto_verify_license(
        license: char_p::Ref<'_>,
        out_err_msg: Option<Out<'_, Option<char_p::Box>>>,
    ) -> LicenseVerificationResult;

    pub fn ditto_run_garbage_collection(ditto: &'_ Ditto);

    pub fn ditto_disable_sync_with_v3(ditto: &'_ Ditto) -> c_int;

    pub fn ditto_get_collection_names(ditto: &'_ Ditto) -> c::Result_Vec<char_p::Box>;

    pub fn ditto_queries_hash<'s, 'l: 's>(
        ditto: &Ditto,
        coll_names: c_slice::Ref<'s, char_p::Ref<'l>>,
        queries: c_slice::Ref<'s, char_p::Ref<'l>>,
    ) -> c::Result<u64>;

    pub fn ditto_queries_hash_mnemonic<'s, 'l: 's>(
        ditto: &'_ Ditto,
        coll_names: c_slice::Ref<'s, char_p::Ref<'l>>,
        queries: c_slice::Ref<'s, char_p::Ref<'l>>,
    ) -> c::Result<char_p::Box>;

    pub fn ditto_collection(ditto: &'_ Ditto, name: char_p::Ref<'_>) -> c_int;

    pub fn ditto_collection_insert_value(
        ditto: &'_ Ditto,
        coll_name: char_p::Ref<'_>,
        doc_cbor: c_slice::Ref<'_, u8>,
        write_strategy: WriteStrategyRs,
        log_hint: Option<char_p::Ref<'_>>,
        txn: Option<&'_ mut CWriteTransaction>,
    ) -> c::Result_BoxedSlice<u8>;

    pub fn ditto_collection_update(
        ditto: &'_ Ditto,
        coll_name: char_p::Ref<'_>,
        transaction: &'_ mut CWriteTransaction,
        document: c::Box<Document>,
    ) -> c_int;

    pub fn ditto_collection_evict(
        ditto: &'_ Ditto,
        coll_name: char_p::Ref<'_>,
        transaction: &'_ mut CWriteTransaction,
        id: c_slice::Ref<'_, u8>,
    ) -> c::Result<bool>;

    pub fn ditto_collection_remove(
        ditto: &'_ Ditto,
        coll_name: char_p::Ref<'_>,
        transaction: &'_ mut CWriteTransaction,
        id: c_slice::Ref<'_, u8>,
    ) -> c::Result<bool>;

    pub fn ditto_collection_get(
        ditto: &'_ Ditto,
        coll_name: char_p::Ref<'_>,
        id: c_slice::Ref<'_, u8>,
        transaction: &'_ mut CReadTransaction,
    ) -> c::Result_Box<Document>;

    pub fn ditto_collection_exec_query_str<'order_ref, 'order: 'order_ref>(
        ditto: &'_ Ditto,
        coll_name: char_p::Ref<'_>,
        txn: Option<&'_ mut CWriteTransaction>,
        query: char_p::Ref<'_>,
        query_args: Option<c_slice::Ref<'_, u8>>,
        order_by_params: c_slice::Ref<'order_ref, COrderByParam<'order>>,
        limit: c_int,
        offset: c_uint,
    ) -> c::Result_Vec<c::Box<Document>>;

    pub fn ditto_collection_remove_query_str<'order_ref, 'order: 'order_ref>(
        ditto: &'_ Ditto,
        coll_name: char_p::Ref<'_>,
        transaction: &'_ mut CWriteTransaction,
        query: char_p::Ref<'_>,
        query_args: Option<c_slice::Ref<'_, u8>>,
        order_by_params: c_slice::Ref<'order_ref, COrderByParam<'order>>,
        limit: c_int,
        offset: c_uint,
    ) -> c::Result_Vec<c_slice::Box<u8>>;

    pub fn ditto_collection_evict_query_str<'order_ref, 'order: 'order_ref>(
        ditto: &'_ Ditto,
        coll_name: char_p::Ref<'_>,
        transaction: &'_ mut CWriteTransaction,
        query: char_p::Ref<'_>,
        query_args: Option<c_slice::Ref<'_, u8>>,
        order_by_params: c_slice::Ref<'order_ref, COrderByParam<'order>>,
        limit: c_int,
        offset: c_uint,
    ) -> c::Result_Vec<c_slice::Box<u8>>;

    pub fn ditto_read_transaction<'txn>(ditto: &'_ Ditto) -> c::Result_Box<CReadTransaction>;

    pub type CReadTransaction;
    #[drop]
    pub fn ditto_read_transaction_free(transaction: c::Box<CReadTransaction>);

    pub fn ditto_write_transaction(
        ditto: &'_ Ditto,
        log_hint: Option<char_p::Ref<'_>>,
    ) -> c::Result_Box<CWriteTransaction>;

    pub type CWriteTransaction;
    #[drop]
    pub fn ditto_write_transaction_free(txn: c::Box<CWriteTransaction>);

    pub fn ditto_write_transaction_commit(
        ditto: &Ditto,
        transaction: c::Box<CWriteTransaction>,
    ) -> c_int;

    pub fn ditto_write_transaction_rollback(ditto: &Ditto, transaction: c::Box<CWriteTransaction>);

    pub fn ditto_document_id(document: &'_ Document) -> c_slice::Box<u8>;

    pub fn ditto_document_id_query_compatible(
        id: c_slice::Ref<'_, u8>,
        string_primitive_format: StringPrimitiveFormat,
    ) -> char_p::Box;

    pub fn ditto_validate_document_id(
        cbor: c_slice::Ref<'_, u8>,
        out_cbor: Out<'_, Option<c_slice::Box<u8>>>,
    ) -> c_uint;

    pub fn ditto_add_subscription<'s, 'l: 's>(
        ditto: &'_ Ditto,
        collection: char_p::Ref<'_>,
        query: char_p::Ref<'_>,
        query_args_cbor: Option<c_slice::Ref<'_, u8>>,
        order_by: c_slice::Ref<'s, COrderByParam<'l>>,
        limit: c_int,
        offset: c_uint,
    ) -> c_int;

    pub fn ditto_remove_subscription<'s, 'l: 's>(
        ditto: &'_ Ditto,
        collection: char_p::Ref<'_>,
        query: char_p::Ref<'_>,
        query_args_cbor: Option<c_slice::Ref<'_, u8>>,
        order_by: c_slice::Ref<'s, COrderByParam<'l>>,
        limit: c_int,
        offset: c_uint,
    ) -> c_int;

    pub fn ditto_live_query_webhook_generate_new_api_secret(ditto: &'_ Ditto) -> c_int;

    pub fn ditto_live_query_webhook_start_all(ditto: &'_ Ditto) -> c_int;

    pub fn ditto_live_query_webhook_start_by_id(
        ditto: &'_ Ditto,
        id: c_slice::Ref<'_, u8>,
    ) -> c_int;

    pub fn ditto_live_query_webhook_register_str<'s, 'l: 's>(
        ditto: &'_ Ditto,
        coll_name: char_p::Ref<'_>,
        query: char_p::Ref<'_>,
        order_by: c_slice::Ref<'s, COrderByParam<'l>>,
        limit: c_int,
        offset: c_uint,
        url: char_p::Ref<'_>,
    ) -> c::Result_BoxedSlice<u8>;

    pub unsafe fn ditto_live_query_register_str<'s, 'l: 's>(
        ditto: &'_ Ditto,
        coll_name: char_p::Ref<'_>,
        query: char_p::Ref<'_>,
        query_args_cbor: Option<c_slice::Ref<'_, u8>>,
        order_by: c_slice::Ref<'s, COrderByParam<'l>>,
        limit: c_int,
        offset: c_uint,
        lq_availability: LiveQueryAvailability,
        ctx: *mut c_void,
        retain: Option<unsafe extern "C" fn(*mut c_void)>,
        release: Option<unsafe extern "C" fn(*mut c_void)>,
        c_cb: unsafe extern "C" fn(ctx: *mut c_void, params: ChangeHandlerWithDocsDiff),
    ) -> c::Result<i64>;

    pub fn ditto_live_query_start(ditto: &'_ Ditto, id: i64) -> c_int;

    pub fn ditto_live_query_stop(ditto: &'_ Ditto, id: i64);

    pub fn ditto_stop_all_live_queries(ditto: &mut Ditto);

    pub fn ditto_live_query_signal_available_next(ditto: &'_ Ditto, id: i64);

    pub fn ditto_documents_hash(documents: c_slice::Ref<'_, c::Box<Document>>) -> c::Result<u64>;

    pub fn ditto_documents_hash_mnemonic(
        documents: c_slice::Ref<'_, c::Box<Document>>,
    ) -> c::Result<char_p::Box>;

    pub fn ditto_new_attachment_from_file(
        ditto: &'_ Ditto,
        source_path: char_p::Ref<'_>,
        file_operation: AttachmentFileOperation,
        out_attachment: Out<'_, Attachment>,
    ) -> c_uint;

    pub fn ditto_get_complete_attachment_path(
        ditto: &'_ Ditto,
        handle: &'_ AttachmentHandle,
    ) -> char_p::Box;

    pub unsafe fn ditto_resolve_attachment(
        ditto: &'_ Ditto,
        id: c_slice::Ref<'_, u8>,
        ctx: *mut c_void,
        retain: Option<unsafe extern "C" fn(*mut c_void)>,
        release: Option<unsafe extern "C" fn(*mut c_void)>,
        on_complete_cb: unsafe extern "C" fn(ctx: *mut c_void, c::Box<AttachmentHandle>),
        on_progress_cb: unsafe extern "C" fn(ctx: *mut c_void, u64, u64),
        on_deleted_cb: unsafe extern "C" fn(ctx: *mut c_void),
    ) -> c::Result<u64>;

    pub fn ditto_cancel_resolve_attachment(
        ditto: &'_ Ditto,
        id: c_slice::Ref<'_, u8>,
        cancel_token: u64,
    ) -> c_uint;

    pub fn ditto_document_update(document: &'_ mut Document, cbor: c_slice::Ref<'_, u8>) -> c_int;

    pub fn ditto_collection_update_multiple(
        ditto: &'_ Ditto,
        coll_name: char_p::Ref<'_>,
        transaction: &'_ mut CWriteTransaction,
        documents: c::Vec<c::Box<Document>>,
    ) -> c_int;

    pub type FfiError: Send + Sync;
    pub fn dittoffi_error_code(_error: &'_ FfiError) -> FfiErrorCode;

    pub fn dittoffi_error_description(_error: &'_ FfiError) -> char_p::Box;

    #[drop]
    pub fn dittoffi_error_free(_error: c::Box<FfiError>);

    pub fn ditto_error_message() -> Option<char_p::Box>;

    pub fn ditto_document_cbor(document: &'_ Document) -> c_slice::Box<u8>;

    pub fn ditto_document_get_cbor_with_path_type(
        document: &'_ Document,
        pointer: char_p::Ref<'_>,
        path_type: PathAccessorType,
    ) -> c::Result_OptionBoxedSlice<u8>;

    pub fn ditto_document_get_variant_cbor(
        document: &'_ Document,
        pointer: char_p::Ref<'_>,
        variant: DittoCrdtType,
    ) -> c::Result_BoxedSlice<u8>;

    pub fn ditto_document_set_cbor(
        document: &'_ mut Document,
        pointer: char_p::Ref<'_>,
        cbor: c_slice::Ref<'_, u8>,
    ) -> c_int;

    pub fn ditto_document_set_cbor_with_timestamp(
        document: &'_ mut Document,
        pointer: char_p::Ref<'_>,
        cbor: c_slice::Ref<'_, u8>,
        timestamp: c_uint,
    ) -> c_int;

    pub fn ditto_document_remove(document: &'_ mut Document, pointer: char_p::Ref<'_>) -> c_int;

    pub type Document: Send + Sync;
    #[drop]
    pub fn ditto_document_free(document: c::Box<Document>);

    pub fn ditto_document_increment_counter(
        document: &'_ mut Document,
        pointer: char_p::Ref<'_>,
        amount: c_double,
    ) -> c_int;

    pub type AttachmentHandle: Send + Sync;
    #[drop]
    pub fn ditto_free_attachment_handle(handle: c::Box<AttachmentHandle>);

    pub type StaticTcpClientPlatformHandle: Send + Sync;
    #[drop]
    pub fn static_tcp_client_free_handle(handle: c::Box<StaticTcpClientPlatformHandle>);

    pub type WebsocketClientPlatformHandle: Send + Sync;
    #[drop]
    pub fn websocket_client_free_handle(handle: c::Box<WebsocketClientPlatformHandle>);

    pub type MdnsClientPlatformHandle: Send + Sync;
    #[drop]
    pub fn mdns_client_free_handle(handle: c::Box<MdnsClientPlatformHandle>);

    pub type MdnsServerPlatformHandle: Send + Sync;
    #[drop]
    pub fn mdns_server_free_handle(handle: c::Box<MdnsServerPlatformHandle>);

    pub type BleClientPlatformHandle: Send + Sync;
    #[drop]
    pub fn ble_client_free_handle(handle: c::Box<BleClientPlatformHandle>);

    pub type BleServerPlatformHandle: Send + Sync;
    #[drop]
    pub fn ble_server_free_handle(handle: c::Box<BleServerPlatformHandle>);

    pub fn ditto_free_documents(documents: Option<c::Vec<c::Box<Document>>>);

    pub fn ditto_free_indices(indices: Option<c_slice::Box<usize>>);

    pub fn ditto_c_string_free(s: char_p::Box);

    pub fn ditto_c_bytes_free(bytes: c_slice::Box<u8>);

    pub fn ditto_identity_config_make_online_with_authentication(
        app_id: char_p::Ref<'_>,
        base_url: char_p::Ref<'_>,
    ) -> c::Result_Box<IdentityConfig>;

    pub fn ditto_identity_config_make_offline_playground(
        app_id: char_p::Ref<'_>,
        site_id: c_ulonglong,
    ) -> c::Result_Box<IdentityConfig>;

    pub fn ditto_identity_config_make_online_playground(
        app_id: char_p::Ref<'_>,
        shared_token: char_p::Ref<'_>,
        base_url: char_p::Ref<'_>,
    ) -> c::Result_Box<IdentityConfig>;

    pub fn ditto_identity_config_make_shared_key(
        app_id: char_p::Ref<'_>,
        key_der_b64: char_p::Ref<'_>,
        site_id: c_ulonglong,
    ) -> c::Result_Box<IdentityConfig>;

    pub fn ditto_identity_config_make_manual(
        manual_identity_str: char_p::Ref<'_>,
    ) -> c::Result_Box<IdentityConfig>;

    pub fn ditto_identity_config_make_manual_v0(
        config_cbor_b64: char_p::Ref<'_>,
    ) -> c::Result_Box<IdentityConfig>;

    pub fn ditto_auth_set_login_provider(ditto: &'_ Ditto, login_provider: BoxedLoginProvider);

    pub fn ditto_auth_client_get_site_id(ditto: &'_ Ditto) -> c_ulonglong;

    pub fn ditto_auth_client_get_app_id(ditto: &'_ Ditto) -> char_p::Box;

    pub fn ditto_auth_client_user_id(ditto: &'_ Ditto) -> Option<char_p::Box>;

    pub unsafe fn ditto_auth_client_make_login_provider(
        ctx: *mut c_void,
        retain: Option<unsafe extern "C" fn(*mut c_void)>,
        release: Option<unsafe extern "C" fn(*mut c_void)>,
        expiring_cb: unsafe extern "C" fn(*mut c_void, c_uint),
    ) -> BoxedLoginProvider;

    pub unsafe fn ditto_auth_client_set_validity_listener(
        ditto: &'_ Ditto,
        ctx: *mut c_void,
        retain: Option<unsafe extern "C" fn(*mut c_void)>,
        release: Option<unsafe extern "C" fn(*mut c_void)>,
        validity_update_cb: Option<unsafe extern "C" fn(*mut c_void, c_int, c_int)>,
    );

    pub fn ditto_auth_client_is_web_valid(ditto: &'_ Ditto) -> c_int;

    pub fn ditto_auth_client_is_x509_valid(ditto: &'_ Ditto) -> c_int;

    pub fn dittoffi_presence_try_set_peer_metadata_json(
        ditto: &'_ Ditto,
        peer_info: c_slice::Ref<'_, u8>,
    ) -> FfiResult<()>;

    pub type FfiConnectionRequest: Send + Sync;
    #[drop]
    pub fn dittoffi_connection_request_free(__: c::Box<FfiConnectionRequest>);

    pub fn dittoffi_connection_request_peer_metadata_json(
        r: &FfiConnectionRequest,
    ) -> c_slice::Ref<'_, u8>;

    pub fn dittoffi_connection_request_identity_service_metadata_json(
        r: &FfiConnectionRequest,
    ) -> c_slice::Ref<'_, u8>;

    pub fn dittoffi_connection_request_peer_key_string(r: &FfiConnectionRequest) -> char_p::Box;

    pub fn dittoffi_connection_request_authorize(
        r: &FfiConnectionRequest,
        authorized: ConnectionRequestAuthorization,
    );

    pub fn dittoffi_connection_request_connection_type(r: &FfiConnectionRequest) -> ConnectionType;

    pub fn dittoffi_presence_set_connection_request_handler(
        ditto: &'_ Ditto,
        ffi_handler: Option<VirtualPtr<dyn Send + Sync + FfiConnectionRequestHandler>>,
    );

    pub fn dittoffi_presence_peer_metadata_json(ditto: &'_ Ditto) -> c_slice::Box<u8>;

    pub fn ditto_auth_client_login_with_credentials(
        ditto: &'_ Ditto,
        username: char_p::Ref<'_>,
        password: char_p::Ref<'_>,
        provider: char_p::Ref<'_>,
    ) -> c_int;

    pub fn ditto_auth_client_login_with_token_and_feedback(
        ditto: &'_ Ditto,
        token: char_p::Ref<'_>,
        provider: char_p::Ref<'_>,
    ) -> c::Result_OptionBoxedCharPtr;

    pub fn ditto_auth_client_login_with_token(
        ditto: &'_ Ditto,
        token: char_p::Ref<'_>,
        provider: char_p::Ref<'_>,
    ) -> c_int;

    pub fn ditto_auth_client_logout(ditto: &'_ Ditto) -> c_int;

    pub type LoginProvider: Send + Sync;
    #[drop]
    pub fn ditto_auth_login_provider_free(login_provider: c::Box<LoginProvider>);

    pub fn ditto_insert_timeseries_event(
        ditto: &'_ Ditto,
        timestamp: [u8; 8], // a u64 ... but arm32
        nanos: u32,
        ts_name: char_p::Ref<'_>,
        cbor: c_slice::Ref<'_, u8>,
        txn: Option<&'_ mut CWriteTransaction>,
    ) -> c_int;

    pub fn ditto_set_sync_group(ditto: &'_ Ditto, sync_group: c_uint);

    pub fn ditto_set_device_name(ditto: &'_ Ditto, device_name: char_p::Ref<'_>);

    pub fn ditto_set_connect_retry_interval(ditto: &'_ Ditto, retry_interval_millis: u32);

    pub unsafe fn ditto_register_presence_v2_callback(
        ditto: &'_ Ditto,
        ctx: *mut c_void,
        retain: Option<unsafe extern "C" fn(*mut c_void)>,
        release: Option<unsafe extern "C" fn(*mut c_void)>,
        c_cb: Option<unsafe extern "C" fn(*mut c_void, json: char_p::Ref<'_>)>,
    );

    pub fn ditto_presence_v2(ditto: &'_ Ditto) -> char_p::Box;

    pub unsafe fn ditto_register_presence_callback_v3(
        ditto: &'_ Ditto,
        ctx: *mut c_void,
        retain: Option<unsafe extern "C" fn(*mut c_void)>,
        release: Option<unsafe extern "C" fn(*mut c_void)>,
        c_cb: Option<unsafe extern "C" fn(*mut c_void, json: char_p::Ref<'_>)>,
    );

    pub fn ditto_presence_v3(ditto: &'_ Ditto) -> char_p::Box;

    pub type DiskUsageObserver: Send + Sync;
    #[drop]
    pub fn ditto_release_disk_usage_callback(_handle: c::Box<DiskUsageObserver>);

    pub unsafe fn ditto_register_disk_usage_callback(
        ditto: &'_ Ditto,
        component: FsComponent,
        ctx: *mut c_void,
        retain: Option<unsafe extern "C" fn(*mut c_void)>,
        release: Option<unsafe extern "C" fn(*mut c_void)>,
        c_cb: Option<unsafe extern "C" fn(*mut c_void, cbor: c_slice::Ref<'_, u8>)>,
    ) -> Option<c::Box<DiskUsageObserver>>;

    pub fn ditto_disk_usage(ditto: &'_ Ditto, path: FsComponent) -> c_slice::Box<u8>;

    pub fn register_executor_functions(fns: ExecutorFunctions);

    pub type QueryResultItem: Send + Sync;
    pub fn dittoffi_query_result_item_cbor(_dql_result: &'_ QueryResultItem) -> c_slice::Box<u8>;
    pub fn dittoffi_query_result_item_json(_dql_result: &'_ QueryResultItem) -> char_p::Box;
    #[drop]
    pub fn dittoffi_query_result_item_free(_dql_result: c::Box<QueryResultItem>);

    pub type QueryResult: Send + Sync;
    pub fn dittoffi_query_result_item_count(response: &'_ QueryResult) -> usize;
    pub fn dittoffi_query_result_item_at(
        response: &'_ QueryResult,
        index: usize,
    ) -> c::Box<QueryResultItem>;
    pub fn dittoffi_query_result_mutated_document_id_count(response: &'_ QueryResult) -> usize;
    pub fn dittoffi_query_result_mutated_document_id_at(
        response: &'_ QueryResult,
        index: usize,
    ) -> c_slice::Box<u8>;
    #[drop]
    pub fn dittoffi_query_result_free(_dql_reponse: c::Box<QueryResult>);

    pub fn dittoffi_try_exec_statement(
        ditto: &'_ Ditto,
        write_txn: Option<&'_ mut CWriteTransaction>,
        query: char_p::Ref<'_>,
        query_args_cbor: Option<c_slice::Ref<'_, u8>>,
    ) -> c::FfiResultBox<QueryResult>;

    pub unsafe fn dittoffi_try_experimental_register_change_observer_str(
        ditto: &'_ Ditto,
        query: char_p::Ref<'_>,
        query_args_cbor: Option<c_slice::Ref<'_, u8>>,
        lq_availability: LiveQueryAvailability,
        ctx: *mut c_void,
        retain: Option<unsafe extern "C" fn(*mut c_void)>,
        release: Option<unsafe extern "C" fn(*mut c_void)>,
        c_cb: unsafe extern "C" fn(ctx: *mut c_void, params: ChangeHandlerWithQueryResult),
    ) -> c::Result<i64>;

    pub fn dittoffi_try_add_sync_subscription(
        ditto: &'_ Ditto,
        query: char_p::Ref<'_>,
        query_args_cbor: Option<c_slice::Ref<'_, u8>>,
    ) -> c::FfiResult<()>;

    pub fn dittoffi_try_remove_sync_subscription(
        ditto: &'_ Ditto,
        query: char_p::Ref<'_>,
        query_args_cbor: Option<c_slice::Ref<'_, u8>>,
    ) -> c::FfiResult<()>;

    pub fn ditto_small_peer_info_get_is_enabled(ditto: &'_ Ditto) -> bool;
    pub fn ditto_small_peer_info_set_enabled(ditto: &'_ Ditto, enabled: bool);

    pub fn ditto_small_peer_info_get_sync_scope(ditto: &'_ Ditto) -> DittoSmallPeerInfoSyncScope;
    pub fn ditto_small_peer_info_set_sync_scope(
        ditto: &'_ Ditto,
        sync_scope: DittoSmallPeerInfoSyncScope,
    );

    pub fn ditto_small_peer_info_get_metadata(ditto: &'_ Ditto) -> char_p::Box;
    pub fn ditto_small_peer_info_set_metadata(ditto: &'_ Ditto, metadata: char_p::Ref<'_>) -> i32;
}

impl fmt::Debug for c::Box<Ditto> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Ditto").finish()
    }
}

impl FfiDrop for c::Box<IdentityConfig> {
    unsafe fn drop(&mut self) {
        // We don't expose this functionality in the API, since it is to be
        // given to the final `Ditto` constructor anyway
        panic!("A `c::Box<IdentityConfig>` is to be fed to `ditto_make`")
    }
}

#[repr(C)]
pub enum ConnectionRequestAuthorization {
    Deny,

    Allow,
}

#[non_exhaustive]
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Platform {
    Windows,
    Mac,
    Ios,
    Android,
    Linux,
    Unknown,
}

#[repr(C)]
pub struct FfiResult<T: ::safer_ffi::layout::ReprC> {
    pub error: Option<c::Box<FfiError>>,
    pub success: T::CLayout,
}

crate::ffi_utils::non_exhaustive_ffi_enum! {
    // Note(Daniel): keep up-to-date with `ffi/src/result/error.rs`!
    #[repr(i32)]
    #[derive(Clone, PartialEq, Eq)]
    pub enum FfiErrorCode {
        CborInvalid,
        CborUnsupported,
        StoreDocumentId,
        StoreDocumentNotFound,
        StoreDatabase,
        StoreQuery,
        ParameterQuery,
        Crdt,
        JsFloatingStoreOperation,
        DqlQueryCompilation,
        DqlInvalidQueryArgs,
        DqlUnsupported,
        ValidationDepthLimitExceeded,
        ValidationNotAMap,
        ValidationSizeLimitExceeded,
        ValidationInvalidJson,
        ValidationInvalidCbor,
        Base64Invalid,
        LockedDittoWorkingDirectory,
        FilesystemError,
        Internal,
    }
}

// Keepin sync with core/presence/src/types.rs
crate::ffi_utils::non_exhaustive_ffi_enum! {
    #[repr(i32)]
    #[derive(Clone, PartialEq, Eq)]
    pub enum ConnectionType {
        Bluetooth,
        AccessPoint,
        P2PWiFi,
        WebSocket,
    }
}

#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DittoErrorCode {
    Ok = 0x0000000,
    Unknown = 0x0000001,
    NotImplemented = 0x0000002,
    // global ditto errors =    0x1000000,
    FailedToAcquireLockFile = 0x1000001,
    // Encryption errors =      0x2000000,
    InvalidPassphrase = 0x2000001,
    ExtraneousPassphraseGiven = 0x2000002,
    PassphraseNotGiven = 0x2000003,
    AlreadyEncrypted = 0x2000004,
    EncryptionFailed = 0x2000005,
    CannotBeEncrypted = 0x2000006,
    NotInitialized = 0x2000007,
}

#[non_exhaustive]
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(dead_code)]
pub enum Language {
    Swift,
    ObjectiveC,
    CPlusPlus,
    CSharp,
    Javascript,
    Unknown,
    Rust,
}

#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CLogLevel {
    // Starts at 1 to match the Rust log levels which have an `Off` case with
    // value 0
    Error = 1,
    Warning,
    Info,
    Debug,
    Verbose,
}

#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum WebSocketMode {
    Enabled,
    Disabled,
}

#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HistoryTracking {
    Enabled,
    Disabled,
}

#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(dead_code)]
pub enum LicenseVerificationResult {
    LicenseOk = 0,
    VerificationFailed = -1,
    LicenseExpired = -2,
    UnsupportedFutureVersion = -3,
}

#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum WriteStrategyRs {
    Merge,
    InsertIfAbsent,
    InsertDefaultIfAbsent,
}

#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FsComponent {
    Root = 0,
    Store,
    Auth,
    Replication,
    Attachment,
}

#[derive(Debug, Clone)]
#[repr(C)]
pub struct COrderByParam<'query> {
    pub query_c_str: char_p::Ref<'query>,
    pub direction: QuerySortDirection,
}
impl NonOpaque for COrderByParam<'_> {}

#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(dead_code)]
pub enum QuerySortDirection {
    Ascending = 1,
    Descending,
}

#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DittoCrdtType {
    Counter = 0,
    Register = 1,
    Attachment = 2,
    Rga = 3,
    RWMap = 4,
}

#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum PathAccessorType {
    String,
    Number,
    Int,
    UInt,
    Float,
    Double,
    Bool,
    Null,
    Object,
    Array,
    Any,

    // CRDT types
    Counter,
    Register,
    Attachment,
    // This isn't ever used as things stand, but is here for completeness and to
    // make things easier in future if we do want to support it
    RWMap,
}

impl fmt::Debug for c::Box<Document> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let c_str = ditto_document_id(self);
        // TODO: FIXME
        f.debug_struct("Document").field("id", &c_str).finish()
    }
}

#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(dead_code)]
pub enum LiveQueryAvailability {
    Always,
    WhenSignalled,
}

#[repr(C)]
#[allow(nonstandard_style)]
pub struct ChangeHandlerWithDocsDiff {
    /// Must be freed with `ditto_free_documents`.
    pub documents: c::Vec<c::Box<Document>>,
    pub is_initial: bool,
    /// Must be freed with `ditto_free_documents`.
    pub old_documents: Option<c::Vec<c::Box<Document>>>,
    /// Must be freed using `ditto_free_indices`.
    pub insertions: Option<c_slice::Box<usize>>,
    /// Must be freed using `ditto_free_indices`.
    pub deletions: Option<c_slice::Box<usize>>,
    /// Must be freed using `ditto_free_indices`.
    pub updates: Option<c_slice::Box<usize>>,
    /// Must be freed using `ditto_free_indices`.
    pub moves: Option<c_slice::Box<usize>>,
}

#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(dead_code)]
pub enum AttachmentFileOperation {
    Copy = 1,
    Move,
}

#[repr(C)]
pub struct Attachment {
    pub id: c_slice::Box<u8>,
    pub len: u64,
    pub handle: c::Box<AttachmentHandle>,
}

impl fmt::Debug for c::Box<AttachmentHandle> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("AttachmentHandle").finish()
    }
}

#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum StringPrimitiveFormat {
    WithQuotes,
    WithoutQuotes,
}

#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
/// Which peers to replicate the `__small_peer_info` collection to.
pub enum DittoSmallPeerInfoSyncScope {
    BigPeerOnly,
    LocalPeerOnly,
}

pub trait IntoRustResult {
    type Ok;

    fn into_rust_result(_: Self) -> Result<Self::Ok, ::core::num::NonZeroI32>;
}

// Dyn/virtual Tokio executors part

pub type FfiHandle = VirtualPtr<dyn Send + Sync + FfiFutureExecutor>;

/// Keep in sync with `/utils/utils/src/executor/ffi.rs`.
#[derive_ReprC]
#[repr(C)]
pub struct ExecutorFunctions {
    pub get_current_handle: extern "C" fn() -> FfiHandle,
    pub block_in_place: BlockInPlace,
}

#[rustfmt::skip]
#[derive(Clone, Copy)]
#[derive_ReprC]
#[repr(transparent)]
pub struct BlockInPlace /* = */ (
    pub
    for<'f> // <- supporting this requires the transparent newtype.
    extern "C" fn(func: VirtualPtr<dyn 'f + FfiFnMut>),
);

#[derive_ReprC]
#[repr(C)]
pub struct ChangeHandlerWithQueryResult {
    /// Must be freed with `dittoffi_query_result_free`.
    pub query_result: c::Box<QueryResult>,
}

#[derive_ReprC(dyn)]
pub trait FfiFnMut {
    fn call(&mut self);
}

impl<F: FnMut()> FfiFnMut for F {
    fn call(&mut self) {
        self()
    }
}

// Note(Daniel): keep up-to-date with `ffi/src/auth/mod.rs`
#[derive_ReprC(dyn, Clone)]
pub trait FfiConnectionRequestHandler: 'static + Send + Sync {
    fn on_connecting(&self, connection_request: c::Box<FfiConnectionRequest>);
}

impl<F> FfiConnectionRequestHandler for F
where
    F: 'static + Send + Sync + Fn(c::Box<FfiConnectionRequest>),
{
    fn on_connecting(&self, connection_request: c::Box<FfiConnectionRequest>) {
        self(connection_request)
    }
}
