#ifndef _diffusion_remote_servers_h_
#define _diffusion_remote_servers_h_ 1

/*
 * Copyright © 2020 - 2024 DiffusionData Ltd., All Rights Reserved.
 *
 * Use is subject to licence terms.
 *
 * NOTICE: All information contained herein is, and remains the
 * property of DiffusionData. The intellectual and technical
 * concepts contained herein are proprietary to DiffusionData and
 * may be covered by U.S. and Foreign Patents, patents in process, and
 * are protected by trade secret or copyright law.
 */

/**
 * @file remote-servers.h
 *
 * @addtogroup PublicAPI_RemoteServers
 *
 * This feature allows a client session to manage remote servers.
 *
 * This feature provides the ability to configure the various modes of operation
 * for the use of remote topic views. This is the ability for a topic view
 * specification to indicate that the source topics for the view are to come
 * from another server in a different Diffusion cluster. The server where the
 * topic views are configured is referred to as the 'secondary server' and the
 * server where the actual topics are is referred to as the 'primary server'.
 *
 * <h3>Outbound Connection from the Secondary Server</h3>
 *
 * The typical configuration for a remote server is that there is only
 * configuration at the secondary server (the configuration is automatically
 * distributed to all members of the secondary cluster). In this case, each
 * secondary server connects to a server in the primary cluster (typically via a
 * load-balancer).
 *
 * Remote topic views can specify the use of such remote servers by name. The
 * connection and disconnection is handled automatically by the server (or
 * servers in the same cluster) where the remote servers are defined.
 *
 * A component can specify a remote server by name even if it does not exist
 * (has not yet been created) and when the remote server is created the
 * connection will take place automatically.
 *
 * If a remote server is removed and there are topic views that depend upon it,
 * those topic views will be disabled.
 *
 * This form of connection is provided by a Remote Server of type
 * SECONDARY_INITIATOR.
 *
 * Such a remote server can be built using a `DIFFUSION_REMOTE_SERVER_BUILDER_T`,
 * via `diffusion_remote_server_builder_create_secondary_initiator`. It may then
 * be added to the server (cluster) using `diffusion_create_remote_server`.
 *
 * In this mode a connection from secondary to primary server is only maintained
 * when there is a topic view that depends upon it. There will be no connections
 * if there are no topic views that specify the remote server.
 *
 * <h3>Outbound Connection from the Primary Server</h3>
 *
 * In some cases it may be preferred that the connection is initiated by the
 * primary server, connecting to the secondary server cluster. In this case a
 * single primary server will connect to all members of the secondary cluster.
 *
 * This form of connection is provided by a Remote Server of type `PRIMARY_INITIATOR`.
 * This can be built using a `DIFFUSION_REMOTE_SERVER_BUILDER_T`, via
 * `diffusion_remote_server_builder_create_primary_initiator`. It may then be added
 * to the primary server (cluster) using `diffusion_create_remote_server`.
 *
 * Secondly a Remote Server of type `SECONDARY_ACCEPTOR` should be created
 * in the secondary server (cluster) with the same name as the primary
 * initiator. Such a remote server can be built using a `DIFFUSION_REMOTE_SERVER_BUILDER_T`
 * via `diffusion_remote_server_builder_create_secondary_acceptor`. It may then be added to the
 * secondary server (cluster) using `diffusion_create_remote_server`.
 *
 * Unlike the secondary initiator mode, this mode of connection will establish
 * connections even if there are no topic views in the secondary server
 * (cluster) that name the remote server. If the connection is lost any topic
 * views that depend upon it will be disabled and the primary initiator will
 * attempt to re-establish the connection(s). Topic views depending upon the
 * remote server will only be enabled when the connection is re-established.
 *
 * <h3>Remote Server persistence and replication</h3>
 *
 * Remote server configurations created through this feature are replicated
 * across a cluster and persisted to disk.
 *
 * <h3>Access control</h3>
 *
 * The following access control restrictions are applied:
 *
 * <ul>
 *  <li>To create, remove or check a remote server, a session needs the
 *      `GLOBAL_PERMISSION_CONTROL_SERVER` permission.
 *  <li> To list remote servers, a session needs the
 *      `GLOBAL_PERMISSION_VIEW_SERVER` permission.
 * </ul>
 */

#include "diffusion-api-error.h"
#include "hash_num.h"
#include "misc/deprecate.h"
#include "session.h"

typedef enum
{
    /**
     * Specifies the connection timeout session attribute value (in
     * milliseconds).
     *
     * If a value is not specified `DIFFUSION_DEFAULT_CONNECTION_TIMEOUT`
     * is used.
     */
    DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_CONNECTION_TIMEOUT = 1,

    /**
     * Specifies the input buffer size session attribute.
     *
     * This is the size of the input buffer to use for the connection
     * with the remote server. It is used to receive messages from the
     * remote server. This should be set to the same size as the output
     * buffer used at the remote server.
     *
     * If not specified, a default of 1024k is used.
     */
    DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_INPUT_BUFFER_SIZE = 2,

    /**
     * Specifies the maximum queue size session attribute.
     *
     * This is the maximum number of messages that can be queued to send
     * to the remote server. If this number is exceeded, the connection
     * will be closed. This must be sufficient to cater for messages
     * that may be queued whilst disconnected (awaiting reconnect).
     *
     * The default value is 10,000 messages.
     */
    DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_MAXIMUM_QUEUE_SIZE = 3,

    /**
     * Specifies the output buffer size session attribute.
     *
     * This is the size of the output buffer to use for the connection
     * with the remote server. It is used to send messages to the remote
     * server. This should be set to the same size as the input buffer
     * used by the remote server.
     *
     * If not specified, a default of 1024k is used.
     */
    DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_OUTPUT_BUFFER_SIZE = 4,

    /**
     * Specifies the reconnection timeout session attribute.
     *
     * This is the total time in milliseconds that will be allowed to
     * reconnect a failed connection.
     *
     * For reconnection to work, the remove server connector must have
     * been configured to support reconnection
     *
     * If a value is not specified `DIFFUSION_DEFAULT_RECONNECTION_TIMEOUT`
     * is used.
     *
     * This value cannot be supplied for a Secondary Acceptor Remote Server.
     */
    DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_RECONNECTION_TIMEOUT = 5,

    /**
     * Specifies the recovery buffer size session attribute.
     *
     * If the remote server is configured to support reconnection, a
     * session established with a non-zero reconnect-timeout retains a
     * buffer of sent messages. If the session disconnects and
     * reconnects, this buffer is used to re-send messages that the
     * server has not received.
     *
     * The default value is 10,000 messages. If reconnect-timeout is 0
     * then this value is ignored.
     *
     * This value cannot be supplied for a Secondary Acceptor Remote Server.
     */
    DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_RECOVERY_BUFFER_SIZE = 6,

    /**
     * Specifies the delay after losing a connection before attempting
     * a reconnection.
     *
     * The value is specified in milliseconds, Default 1000 (1 second)
     *
     * This value cannot be supplied for a Secondary Acceptor Remote Server.
     */
    DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_RETRY_DELAY = 7,

    /**
     * Specifies the write timeout session attribute value (in
     * milliseconds).
     *
     * If a value is not specified `DIFFUSION_DEFAULT_WRITE_TIMEOUT`
     * is used.
     */
    DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_WRITE_TIMEOUT = 8
} DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_T;

typedef enum
{
    /**
     * The connection is inactive.
     *
     * This means that the remote server can successfully connect but a
     * physical connection is not being maintained as there are no
     * components that require the remote server.
     *
     * If in an inactive or failed state, a test connection will have
     * been tried to check that the connection can be made and the
     * connection will then have been closed.
     */
    DIFFUSION_REMOTE_SERVER_CONNECTION_STATE_INACTIVE = 1,

    /**
     * The remote server is connected and actively in use by components
     * that require it.
     */
    DIFFUSION_REMOTE_SERVER_CONNECTION_STATE_CONNECTED = 2,

    /**
     * The connection has failed but a retry is scheduled.
     * In this case `diffusion_check_remote_server_response_get_failure_message`
     * will provide details of the failure that resulted in a retry.
     */
    DIFFUSION_REMOTE_SERVER_CONNECTION_STATE_RETRYING = 3,

    /**
     *  The connection failed to establish.
     * If the connection was in an inactive or failed state state, a
     * test connection was tried and failed.
     *
     * In this case `diffusion_check_remote_server_response_get_failure_message`
     * will provide more detail.
     */
    DIFFUSION_REMOTE_SERVER_CONNECTION_STATE_FAILED = 4,

    /**
     * The named remote server did not exist.
     */
    DIFFUSION_REMOTE_SERVER_CONNECTION_STATE_MISSING = 5
} DIFFUSION_REMOTE_SERVER_CONNECTION_STATE_T;

typedef enum
{
    /**
     * Secondary initiator remote server.
     *
     * Defined on secondary servers.
     *
     * Secondary remote server that initiates a connection to a primary server.
     *
     * This type makes a connection from a secondary server (or each secondary
     * cluster member) to a primary server. No remote server definition is
     * required at the primary server.
     *
     * @since 6.10
     */
    DIFFUSION_REMOTE_SERVER_TYPE_SECONDARY_INITIATOR = 1,

    /**
     * Primary initiator.
     *
     * Defined on primary servers.
     *
     * This type makes a connection from a primary server (cluster) to a
     * secondary server (or all secondary cluster members) with a
     * Secondary Acceptor Remote Server of the same name.
     *
     * @since 6.10
     */
    DIFFUSION_REMOTE_SERVER_TYPE_PRIMARY_INITIATOR = 2,

    /**
     * Secondary acceptor remote server.
     *
     * Defined on secondary servers.
     *
     * Secondary remote server that accepts a connection from a primary server.
     *
     * This type accepts a connection from a Primary Initiator with the
     * same name configured at the primary server (cluster).
     *
     * @since 6.10
     */
    DIFFUSION_REMOTE_SERVER_TYPE_SECONDARY_ACCEPTOR = 3,
} DIFFUSION_REMOTE_SERVER_TYPE_T;


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * Opaque remote server struct.
 *
 * @since 6.7
 */
typedef struct DIFFUSION_REMOTE_SERVER_T DIFFUSION_REMOTE_SERVER_T;


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Returns the remote server type.
 *
 * @param remote_server the remote server.
 *
 * @return              the type of remote server.
 *
 * @since 6.10
 */
DIFFUSION_REMOTE_SERVER_TYPE_T diffusion_remote_server_get_type(DIFFUSION_REMOTE_SERVER_T *remote_server);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Returns the remote server name.
 *
 * `free` should be called on this pointer when no longer needed.
 *
 * @param remote_server the remote server.
 *
 * @return              the remote server name.
 *
 * @since 6.7
 *
 */
char *diffusion_remote_server_get_name(DIFFUSION_REMOTE_SERVER_T *remote_server);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Returns the urls for connection to secondary servers.
 *
 *  This method is only applicable to `DIFFUSION_REMOTE_SERVER_TYPE_PRIMARY_INITIATOR`,
 *  and will return NULL otherwise.
 *
 * `list_free(free)` should be called on this pointer when no longer needed, if not NULL.
 *
 * @param remote_server         the primary initiator remote server.
 *
 * @return                      the list of urls for connection to secondary servers.
 *
 * @since 6.10
 */
LIST_T *diffusion_remote_server_get_urls(DIFFUSION_REMOTE_SERVER_T *remote_server);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Returns the connector that the primary initiator will use to
 * establish a connection between the secondary server and the primary
 * server.
 *
 * This method is only applicable to `DIFFUSION_REMOTE_SERVER_TYPE_PRIMARY_INITIATOR`,
 * and will return NULL otherwise.
 *
 * `free` should be called on this pointer when no longer needed, if not NULL.
 *
 * @param remote_server         the primary initiator remote server.
 *
 * @return                      the connector name.
 *
 * @since 6.10
 */
char *diffusion_remote_server_get_connector(DIFFUSION_REMOTE_SERVER_T *remote_server);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Returns the interval in milliseconds between connection retries.
 *
 * This method is only applicable to `DIFFUSION_REMOTE_SERVER_TYPE_PRIMARY_INITIATOR`,
 * and will return -1 otherwise.
 *
 * If a primary initiator cannot connect to a secondary server, or loses
 * the connection, this is the amount of time before it will try to
 * connect again
 *
 * @param remote_server         the primary initiator remote server.
 *
 * @return                      the retry delay time in milliseconds.
 *
 * @since 6.10
 */
int diffusion_remote_server_get_retry_delay(DIFFUSION_REMOTE_SERVER_T *remote_server);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Returns the url for connection to primary server.
 *
 * This method is only applicable to `DIFFUSION_REMOTE_SERVER_TYPE_SECONDARY_INITIATOR`,
 * and will return NULL otherwise.
 *
 * `free` should be called on this pointer when no longer needed.
 *
 * @param remote_server the remote server.
 *
 * @return              the url for connection to primary server.
 *
 * @since 6.7
 */
char *diffusion_remote_server_get_url(DIFFUSION_REMOTE_SERVER_T *remote_server);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Returns the principal used for the remote server connection.
 *
 * This method is only applicable to `DIFFUSION_REMOTE_SERVER_TYPE_SECONDARY_INITIATOR`
 * or `DIFFUSION_REMOTE_SERVER_TYPE_SECONDARY_ACCEPTOR`, and will return NULL otherwise.
 *
 * `free` should be called on this pointer when no longer needed.
 *
 * @param remote_server the remote server.
 *
 * @return              the principal used for the remote server connection.
 *
 * @since 6.7
 */
char *diffusion_remote_server_get_principal(DIFFUSION_REMOTE_SERVER_T *remote_server);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Returns the credentials to use to authenticate the connection.
 *
 * This method is only applicable to `DIFFUSION_REMOTE_SERVER_TYPE_SECONDARY_INITIATOR`
 * or `DIFFUSION_REMOTE_SERVER_TYPE_SECONDARY_ACCEPTOR`, and will return NULL otherwise.
 *
 * `credentials_free` should be called on this pointer when no longer needed.
 *
 * @param remote_server the remote server.
 *
 * @return              the credentials to use.
 *
 * @since 6.10
 */
CREDENTIALS_T *diffusion_remote_server_get_credentials(DIFFUSION_REMOTE_SERVER_T *remote_server);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Returns the remote server connection options.
 *
 * This method is only applicable to `DIFFUSION_REMOTE_SERVER_TYPE_SECONDARY_INITIATOR`
 * or `DIFFUSION_REMOTE_SERVER_TYPE_SECONDARY_ACCEPTOR`, and will return NULL otherwise.
 *
 * `hash_num_free` should be called on this pointer when no longer needed.
 *
 * @param remote_server the remote server.
 *
 * @return              the remote server connection options.
 *
 * @since 6.7
 */
HASH_NUM_T *diffusion_remote_server_get_connection_options(DIFFUSION_REMOTE_SERVER_T *remote_server);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Return the missing topic notification filter expression or NULL if
 *        one has not been specified.
 *
 * This method is only applicable to `DIFFUSION_REMOTE_SERVER_TYPE_SECONDARY_INITIATOR`
 * or `DIFFUSION_REMOTE_SERVER_TYPE_SECONDARY_ACCEPTOR`, and will return NULL otherwise.
 *
 * `free` should be called on this pointer when no longer needed, if not NULL.
 *
 * @param remote_server the remote server.
 *
 * @return              the missing topic notification filter if specified, NULL otherwise.
 *
 * @since 6.7
 */
char *diffusion_remote_server_get_missing_topic_notification_filter(DIFFUSION_REMOTE_SERVER_T *remote_server);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Returns the primary server host name used in SSL validation.
 *
 * This method is only applicable to `DIFFUSION_REMOTE_SERVER_TYPE_SECONDARY_ACCEPTOR`,
 * and will return NULL otherwise.
 *
 * `free` should be called on this pointer when longer needed, if not NULL.
 *
 * @param remote_server the secondary acceptor remote server.
 *
 * @return              the primary server host name.
 *
 * @since 6.10
 */
char *diffusion_remote_get_primary_host_name(DIFFUSION_REMOTE_SERVER_T *remote_server);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Free a remote server.
 *
 * @param remote_server     the remote server.
 *
 * @since 6.7
 */
void diffusion_remote_server_free(DIFFUSION_REMOTE_SERVER_T *remote_server);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * Opaque remote server builder struct.
 *
 * @since 6.7
 */
typedef struct DIFFUSION_REMOTE_SERVER_BUILDER_T DIFFUSION_REMOTE_SERVER_BUILDER_T;


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Initialize a new remote server builder.
 *
 * `diffusion_remote_server_builder_free` should be called on this pointer
 *  when no longer needed.
 *
 * @return          the remote server builder.
 *
 * @since 6.7
 */
DIFFUSION_REMOTE_SERVER_BUILDER_T *diffusion_remote_server_builder_init(void);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Reset the builder.
 *
 * @param builder   the remote server builder.
 *
 * @return          the remote server builder.
 *
 * @since 6.7
 */
DIFFUSION_REMOTE_SERVER_BUILDER_T *diffusion_remote_server_builder_reset(DIFFUSION_REMOTE_SERVER_BUILDER_T *builder);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Specifies the delay after losing a connection before attempting a
 * reconnection.
 *
 * The value is specified in milliseconds. Default 1000 (1 second).
 *
 * For `DIFFUSION_REMOTE_SERVER_PRIMARY_INITIATOR_T` remote servers only.
 *
 * @param builder       the remote server builder.
 * @param delay_ms      delay in milliseconds after losing a connection before
 *                      attempting a reconnection.
 * @param api_error     populated on API error. Can be NULL.
 *
 * @return              the remote server builder, if the operation was successful,
 *                      NULL otherwise. In this case, if a non-NULL `api_error` pointer has
 *                      been provided, this will be populated with the error information and
 *                      should be freed with `diffusion_api_error_free`.
 *
 * @return              the remote server builder.
 *
 * @since 6.10
 */
DIFFUSION_REMOTE_SERVER_BUILDER_T *
diffusion_remote_server_builder_retry_delay(DIFFUSION_REMOTE_SERVER_BUILDER_T *builder, int delay_ms, DIFFUSION_API_ERROR *api_error);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Specifies the name of a principal used by the remote server to
 *        connect to the primary server.
 *
 * The default, if not specified, is the anonymous principal.
 *
 * @param builder       the remote server builder.
 * @param principal     principal name or a zero length string to indicate an
 *                      anonymous connection.
 *
 * @return              the remote server builder.
 *
 * @since 6.7
 */
DIFFUSION_REMOTE_SERVER_BUILDER_T *diffusion_remote_server_builder_principal(DIFFUSION_REMOTE_SERVER_BUILDER_T *builder, char *principal);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Specifies the credentials to use when connecting to the primary server.
 *
 * The default, if not specified, is `NONE`.
 *
 * @param builder       the remote server builder.
 * @param credentials   the credentials to use.
 *
 * @return              the remote server builder.
 *
 * @since 6.7
 */
DIFFUSION_REMOTE_SERVER_BUILDER_T *
diffusion_remote_server_builder_credentials(DIFFUSION_REMOTE_SERVER_BUILDER_T *builder, CREDENTIALS_T *credentials);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Specifies a single connection option.
 *
 * This will add to the options currently specified to this builder,
 * or will replace a value if it has already been specified.
 *
 * @param builder   the remote server builder.
 * @param option    the connection option
 * @param value     the connection option value or NULL to remove the option.
 *
 * @return          the remote server builder.
 *
 * @since 6.7
 */
DIFFUSION_REMOTE_SERVER_BUILDER_T *diffusion_remote_server_builder_connection_option(
    DIFFUSION_REMOTE_SERVER_BUILDER_T *builder,
    DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_T option,
    char *value);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Specifies a map of {@link DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_T} settings.
 *
 * This will replace any options currently set for this builder.
 * Any options not supplied will take their default values.
 * If no connection options are specified, either using this method or
 * {@link diffusion_remote_server_builder_connection_option}
 * then all options will take their default value.
 *
 * @param builder               the remote server builder.
 * @param connection_options    map of options.
 *
 * @return                      the remote server builder.
 *
 * @since 6.7
 */
DIFFUSION_REMOTE_SERVER_BUILDER_T *
diffusion_remote_server_builder_connection_options(DIFFUSION_REMOTE_SERVER_BUILDER_T *builder, HASH_NUM_T *connection_options);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Specifies a topic selector expression which will filter missing topic notifications
 *        propagated from secondary to primary server.
 *
 * {@link SVC_MISSING_TOPIC_REQUEST_T Missing topic notifications} are notified when a client
 * subscribes using a topic selector that matches no existing topics. By specifying a missing
 * topic selector filter, all notifications that match the filter on the secondary server will
 * be propagated to the primary server.
 *
 * A match occurs if the path prefix of the subscription selector matches the path prefix of the
 * specified selector.
 * If no filter is specified then no missing topic notifications will be propagated.
 *
 * The special selector expressions `*.*` may be used to indicate that all missing topic
 * notifications should be propagated.
 *
 * Only the path prefix of the specified selector(s) is considered when matching, therefore any
 * use of regular expressions would be ignored.
 *
 * @param builder   the remote server builder.
 * @param filter    a topic selector expression specifying the filter to apply, or NULL to remove
 *                  any existing filter.
 *
 * @return          the remote server builder.
 *
 * @since 6.7
 */
DIFFUSION_REMOTE_SERVER_BUILDER_T *
diffusion_remote_server_builder_missing_topic_notification_filter(DIFFUSION_REMOTE_SERVER_BUILDER_T *builder, char *filter);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Free a remote server builder.
 *
 * @param builder       the remote server builder.
 *
 * @since 6.7
 */
void diffusion_remote_server_builder_free(DIFFUSION_REMOTE_SERVER_BUILDER_T *builder);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Builds a primary initiator using the current values known to this
 * builder.
 *
 * @param builder       the remote server builder.
 * @param name          the name of the primary initiator which must
 *                      correspond to the name of a Secondary Acceptor Remote Server
 *                      defined on the secondary server.
 * @param urls          the list of URLs to use to initiate connections to
 *                      the secondary servers.
 * @param connector     the name of the connector used to establish the
 *                      connection with the secondary server.
 * @param api_error     populated on API error. Can be NULL.
 *
 * @return              a new primary initiator remote server if the operation was successful,
 *                      NULL otherwise. In this case, if a non-NULL `api_error` pointer has
 *                      been provided, this will be populated with the error information and
 *                      should be freed with `diffusion_api_error_free`.
 *
 * @since 6.10
 */
DIFFUSION_REMOTE_SERVER_T *diffusion_remote_server_builder_create_primary_initiator(
    DIFFUSION_REMOTE_SERVER_BUILDER_T *builder,
    char *name,
    LIST_T *urls,
    char *connector,
    DIFFUSION_API_ERROR *api_error);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Builds a secondary initiator using the current values known to this
 * builder.
 *
 * @param builder       the remote server builder.
 * @param name          the remote server name. This is the name that will be
 *                      specified in topic views.
 * @param url           the URL to use to connect to the primary server.
 * @param api_error     populated on API error. Can be NULL.
 *
 * @return              a new secondary initiator remote server if the operation was successful,
 *                      NULL otherwise. In this case, if a non-NULL `api_error` pointer has
 *                      been provided, this will be populated with the error information and
 *                      should be freed with `diffusion_api_error_free`.
 *
 * @since 6.10
 */
DIFFUSION_REMOTE_SERVER_T *diffusion_remote_server_builder_create_secondary_initiator(
    DIFFUSION_REMOTE_SERVER_BUILDER_T *builder,
    char *name,
    char *url,
    DIFFUSION_API_ERROR *api_error);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Builds a secondary acceptor using the current values known to this
 * builder.
 *
 * @param builder               the remote server builder.
 * @param name                  the remote server name. A primary initiator of the
 *                              same name will be able to connect to this acceptor. This
 *                              is the name that will be specified in topic views.
 * @param primary_host_name     the primary server host name that will be
 *                              used in SSL validation of the primary server
 * @param api_error             populated on API error. Can be NULL.
 *
 * @return                      a new secondary initiator remote server if the operation
 *                              was successful, NULL otherwise. In this case, if a non-NULL
 *                              `api_error` pointer has been provided, this will be populated
 *                              with the error information and should be freed with
 *                              `diffusion_api_error_free`.
 *
 * @since 6.10
 */
DIFFUSION_REMOTE_SERVER_T *diffusion_remote_server_builder_create_secondary_acceptor(
    DIFFUSION_REMOTE_SERVER_BUILDER_T *builder,
    char *name,
    char *primary_host_name,
    DIFFUSION_API_ERROR *api_error);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Creates a remote server struct using the current values known to this builder.
 *
 * @param builder       the remote server builder.
 * @param name          the name of the remote server.
 * @param url           the URL to use to connect to the primary server.
 * @param api_error     populated on API error. Can be NULL.
 *
 * @return  a new remote server struct if the operation was successful, NULL otherwise.
 *          In this case, if a non-NULL `api_error` pointer has been provided, this will
 *          be populated with the error information and should be freed with
 *          `diffusion_api_error_free`.
 *
 * @since 6.7
 *
 * @deprecated since 6.10
 * For backwards compatibility this method is retained for
 * secondary initiator compatibility but will be removed at
 * a future release.
 */
DEPRECATED(
    DIFFUSION_REMOTE_SERVER_T *diffusion_remote_server_builder_create(
        DIFFUSION_REMOTE_SERVER_BUILDER_T *builder,
        char *name,
        char *url,
        DIFFUSION_API_ERROR *api_error));


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Callback when a remote server creation attempt has been made.
 *
 * If the remote server definition is nil, this could
 * mean an error has occurred. These may include:
 * <ul>
 *  <li>RemoteServerAlreadyExists - if a remote server with the given name
 *      already exists
 *  <li>ClusterRepartition - if the cluster was repartitioning
 *  <li>SessionSecurity - if the calling session does not have
 *      `GLOBAL_PERMISSION_CONTROL_SERVER` permission
 *  <li>SessionClosed - if the session is closed
 * </ul>
 *
 * @param remote_server   The remote server created if no errors are returned
 * @param errors          Errors encountered during the attempted creation of the remote server
 * @param context         User supplied context.
 *
 * @return HANDLER_SUCCESS or HANDLER_FAILURE.
 */
typedef int (*on_remote_server_created_cb)(DIFFUSION_REMOTE_SERVER_T *remote_server, LIST_T *errors, void *context);

typedef struct diffusion_create_remote_server_params_s
{
    /// The remote server
    DIFFUSION_REMOTE_SERVER_T *remote_server;
    /// Callback when a remote server creation attempt has been made.
    on_remote_server_created_cb on_remote_server_created;
    /// Callback to handle errors. Can be NULL.
    ERROR_HANDLER_T on_error;
    /// Callback to handle discards. Can be NULL.
    DISCARD_HANDLER_T on_discard;
    /// User supplied context. Can be NULL.
    void *context;
} DIFFUSION_CREATE_REMOTE_SERVER_PARAMS_T;

/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Create a new remote server instance with default connection options.
 *
 * If a remote server with the same name already exists an error will be returned.
 *
 * @param session       The current session. If NULL, this function returns immediately.
 * @param params        Parameters defining the `diffusion_create_remote_server` request
 *                      and callbacks.
 * @param api_error     Populated on API error. Can be NULL.
 *
 * @return true if the operation was successful. False, otherwise. In this case, if
 *         a non-NULL `api_error` pointer has been provided, this will be populated
 *         with the error information and should be freed with `diffusion_api_error_free`.
 */
bool diffusion_create_remote_server(
    SESSION_T *session,
    const DIFFUSION_CREATE_REMOTE_SERVER_PARAMS_T params,
    DIFFUSION_API_ERROR *api_error);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Callback when a remote server removal attempt has been made.
 *
 * @param context         User supplied context.
 *
 * @return HANDLER_SUCCESS or HANDLER_FAILURE.
 */
typedef int (*on_remote_server_removed_cb)(void *context);

typedef struct diffusion_remove_remote_server_params_s
{
    /// The name of the remote server to be removed
    char *name;
    /// Callback when the remote server removal attempt has been made
    on_remote_server_removed_cb on_remote_server_removed;
    /// Callback to handle errors. Can be NULL.
    ERROR_HANDLER_T on_error;
    /// Callback to handle discards. Can be NULL.
    DISCARD_HANDLER_T on_discard;
    /// User supplied context. Can be NULL.
    void *context;
} DIFFUSION_REMOVE_REMOTE_SERVER_PARAMS_T;

/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Remove a named remote server if it exists
 *
 * If the named remote server does not exist the callback
 * will return without an error
 *
 * When a named remote server is removed, any components that specify it
 * would be disabled.
 *
 * @param session       The current session. If NULL, this function returns immediately.
 * @param params        Parameters defining the `diffusion_remove_remote_server` request
 *                      and callbacks.
 * @param api_error     Populated on API error. Can be NULL.
 *
 * @return true if the operation was successful. False, otherwise. In this case, if
 *         a non-NULL `api_error` pointer has been provided, this will be populated
 *         with the error information and should be freed with `diffusion_api_error_free`.
 */
bool diffusion_remove_remote_server(
    SESSION_T *session,
    const DIFFUSION_REMOVE_REMOTE_SERVER_PARAMS_T params,
    DIFFUSION_API_ERROR *api_error);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Callback when a response is received from the server,
 *        returning a list of remote servers.
 *
 * @param remote_servers  The list of remote servers
 * @param context         User supplied context.
 *
 * @return HANDLER_SUCCESS or HANDLER_FAILURE.
 */
typedef int (*on_remote_servers_listed_cb)(LIST_T *remote_servers, void *context);

typedef struct diffusion_list_remote_servers_params_s
{
    /// Callback when the remote server removal attempt has been made
    on_remote_servers_listed_cb on_remote_servers_listed;
    /// Callback to handle errors. Can be NULL.
    ERROR_HANDLER_T on_error;
    /// Callback to handle discards. Can be NULL.
    DISCARD_HANDLER_T on_discard;
    /// User supplied context. Can be NULL.
    void *context;
} DIFFUSION_LIST_REMOTE_SERVERS_PARAMS_T;

/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Lists all the remote servers that have been created.
 *
 * @param session       The current session. If NULL, this function returns immediately.
 * @param params        Parameters defining the `diffusion_list_remote_servers` request
 *                      and callbacks.
 * @param api_error     Populated on API error. Can be NULL.
 *
 * @return true if the operation was successful. False, otherwise. In this case, if
 *         a non-NULL `api_error` pointer has been provided, this will be populated
 *         with the error information and should be freed with `diffusion_api_error_free`.
 */
bool diffusion_list_remote_servers(SESSION_T *session, const DIFFUSION_LIST_REMOTE_SERVERS_PARAMS_T params, DIFFUSION_API_ERROR *api_error);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * Opaque check remote server response struct
 */
typedef struct DIFFUSION_CHECK_REMOTE_SERVER_RESPONSE_T DIFFUSION_CHECK_REMOTE_SERVER_RESPONSE_T;


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Returns the state of the connection of the remote server.
 *
 * @param response  the check remote server response
 *
 * @return          the check remote server response's state
 */
DIFFUSION_REMOTE_SERVER_CONNECTION_STATE_T
diffusion_check_remote_server_response_get_state(DIFFUSION_CHECK_REMOTE_SERVER_RESPONSE_T *response);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Returns a memory allocated copy of the check remote server response failure message.
 *        `free` should be called on this pointer when no longer needed.
 *
 * @param response  the check remote server response
 *
 * @return          the check remote server response's failure message
 */
char *diffusion_check_remote_server_response_get_failure_message(DIFFUSION_CHECK_REMOTE_SERVER_RESPONSE_T *response);


/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Callback when a response is received from the server,
 *        returning the details of the remote server state.
 *
 * @param response  The response from the server
 * @param context   User supplied context.
 *
 * @return HANDLER_SUCCESS or HANDLER_FAILURE.
 */
typedef int (*on_remote_server_checked_cb)(DIFFUSION_CHECK_REMOTE_SERVER_RESPONSE_T *response, void *context);

typedef struct diffusion_check_remote_server_params_s
{
    /// The name of the remote server to be removed
    char *name;
    /// Callback when the remote server removal attempt has been made
    on_remote_server_checked_cb on_remote_server_checked;
    /// Callback to handle errors. Can be NULL.
    ERROR_HANDLER_T on_error;
    /// Callback to handle discards. Can be NULL.
    DISCARD_HANDLER_T on_discard;
    /// User supplied context. Can be NULL.
    void *context;
} DIFFUSION_CHECK_REMOTE_SERVER_PARAMS_T;

/**
 * @ingroup PublicAPI_RemoteServers
 *
 * @brief Checks the current state of a named remote server.
 *
 * @param session       The current session. If NULL, this function returns immediately.
 * @param params        Parameters defining the `diffusion_check_remote_server` request
 *                      and callbacks.
 * @param api_error     Populated on API error. Can be NULL.
 *
 * @return true if the operation was successful. False, otherwise. In this case, if
 *         a non-NULL `api_error` pointer has been provided, this will be populated
 *         with the error information and should be freed with `diffusion_api_error_free`.
 */
bool diffusion_check_remote_server(SESSION_T *session, const DIFFUSION_CHECK_REMOTE_SERVER_PARAMS_T params, DIFFUSION_API_ERROR *api_error);

#endif
