import * as amqp from 'amqplib';
import { EventEmitter } from 'stream';

/**
 * RapidEncoder Interface and Default Implementation
 * Provides methods to encode and decode messages for network transmission.
 */
interface RapidEncoder {
    /**
     * Encodes a message to be sent over the network.
     * @param message - The message to encode.
     * @return {Promise<Buffer>} - The encoded message as a Buffer.
     */
    encode(message: unknown, exchange: string, topic: string): Promise<Buffer>;
    /**
     * Decodes a message received from the network.
     * @param data - The data to decode.
     * @return {Promise<unknown>} - The decoded message.
     */
    decode(data: Buffer, exchange: string, topic: string): Promise<unknown>;
}
/**
 * DefaultRapidEncoder provides a basic implementation of RapidEncoder.
 * It encodes messages as JSON strings and decodes them back to their original form.
 */
declare class DefaultRapidEncoder implements RapidEncoder {
    encode(message: unknown): Promise<Buffer>;
    decode(data: Buffer): Promise<unknown>;
}

/**
 * RapidConnector - Handles connection management to RabbitMQ for the rapid-mq package.
 * Provides methods to connect, disconnect, and access the underlying connection.
 */

/**
 * Options for creating a RapidConnector instance.
 */
interface RapidConnectorOptions {
    /** The RabbitMQ connection URL (e.g., amqp://user:pass@host:port/vhost) */
    url: string;
    /** Unique application identifier for this connector */
    appId: string;
    /** Optional encoder for message serialization */
    encoder?: RapidEncoder;
}
/**
 * RapidConnector provides a simple interface to manage a RabbitMQ connection.
 */
declare class RapidConnector {
    private options;
    private _url;
    private _appId;
    private _connection;
    private _isConnected;
    private _encoder;
    /**
     * Constructs a new RapidConnector.
     * @param options - Configuration options for the connector.
     * @throws {Error} If url or appId is not provided.
     */
    constructor(options: RapidConnectorOptions);
    /**
     * Establishes a connection to RabbitMQ.
     * If already connected, this method does nothing.
     * @throws {Error} If the connection fails.
     */
    connect(): Promise<void>;
    /**
     * Closes the RabbitMQ connection if it is open.
     * If not connected, this method does nothing.
     * @throws {Error} If closing the connection fails.
     */
    disconnect(): Promise<void>;
    /**
     * Returns the active RabbitMQ connection.
     * @throws {Error} If the connection is not established.
     */
    get connection(): amqp.ChannelModel;
    /**
     * Indicates whether the connector is currently connected to RabbitMQ.
     */
    get connected(): boolean;
    /**
     * Returns the application ID associated with this connector.
     */
    get appId(): string;
    get encoder(): RapidEncoder;
}

interface MessagerOptions {
    /** The RapidConnector instance to use for RabbitMQ connection */
    connector: RapidConnector;
    /** (Optional) Name of the exchange to use for messaging */
    exchangeName?: string;
    /** (Optional) Name of the queue to use for messaging */
    durable?: boolean;
    /** (Optional) Whether the queue should be durable */
    exclusive?: boolean;
}
declare class Messager {
    protected _connector: RapidConnector;
    protected _exchangeName: string;
    protected _durable: boolean;
    protected _exclusive: boolean;
    constructor(_connector: RapidConnector, _exchangeName: string, _durable: boolean, _exclusive: boolean);
    get connector(): RapidConnector;
    get exchangeName(): string;
}

/**
 * PubSubMessager - Implements the publish/subscribe messaging pattern for RabbitMQ.
 * Part of the rapid-mq package.
 */

/**
 * Options for PubSubMessager.
 */
interface PubSubMessagerOptions extends MessagerOptions {
    /**
     * Logical group for consumers.
     */
    appGroup: string;
}
/**
 * PubSubMessager - A class that implements the publish/subscribe messaging pattern using RabbitMQ.
 * It allows publishing messages to a topic and subscribing to messages from a topic.
 */
declare class PubSubMessager extends Messager {
    private _appGroup;
    private _channel;
    constructor(options: PubSubMessagerOptions);
    /**
     * The logical group for consumers.
     */
    get appGroup(): string;
    /**
     * Prepare the messager for use by establishing a connection and creating a channel.
     * This method must be called before using the publish or subscribe methods.
     * @throws {Error} If the connection is not established.
     */
    initialize(): Promise<void>;
    /**
     * Publish a message to a specific topic.
     * @param topic - The topic to publish the message to.
     * @param message - The message to publish. It can be any serializable object.
     * @param ttl - Optional time-to-live for the message in milliseconds.
     * @returns {boolean} - Returns true if the message was published successfully.
     * @throws {Error} - Throws an error if the channel is not initialized or if publishing fails.
     */
    publish(topic: string, message: unknown, ttl?: number): Promise<boolean>;
    /**
     * Subscribe to a topic and process incoming messages with the provided callback.
     * @param topic - The topic to subscribe to.
     * @param callback - The function to call when a message is received.
     * @returns {Promise<void>} - Resolves when the subscription is set up.
     * @throws {Error} - Throws an error if the channel is not initialized.
     */
    subscribe(topic: string, callback: (message: unknown) => void): Promise<void>;
}

/**
 * RpcMessager - Implements the request/response (RPC) messaging pattern for RabbitMQ.
 * Allows sending RPC calls and serving RPC methods with automatic correlation and timeout handling.
 */

/**
 * Options for creating an RpcMessager instance.
 */
interface RpcMessagerOptions extends MessagerOptions {
    /** (Optional) Timeout in seconds for RPC calls. Defaults to 5 seconds. */
    timeoutInSec?: number;
    /** (Optional) EventEmitter instance for handling RPC responses. Defaults to a new EventEmitter. */
    emitter?: EventEmitter;
}
/**
 * RpcMessager provides methods to make RPC calls and serve RPC endpoints using RabbitMQ.
 */
declare class RpcMessager extends Messager {
    private _timeout;
    private _channel;
    private _responseQueue;
    private _emitter;
    /**
     * Constructs a new RpcMessager.
     * @param options - Configuration options for the messager.
     * @throws {Error} If connector is not provided.
     */
    constructor(options: RpcMessagerOptions);
    /**
     * Initializes the RpcMessager by creating a channel, asserting the exchange,
     * and setting up a consumer for the reply-to queue.
     * @throws {Error} If the connection is not established.
     */
    initialize(): Promise<void>;
    /**
     * Makes an RPC call to a remote method.
     * @param method - The name of the remote method (routing key).
     * @param args - Arguments to pass to the remote method.
     * @returns Promise<T> - Resolves with the response from the server.
     * @throws {Error} If the channel is not initialized or if the call times out.
     */
    call<T>(method: string, ...args: unknown[]): Promise<T>;
    /**
     * Registers a server (handler) for an RPC method.
     * @param method - The name of the method to serve (routing key).
     * @param callback - The function to handle incoming RPC requests.
     * @returns Promise<void>
     * @throws {Error} If the channel is not initialized.
     */
    server(method: string, callback: (...args: unknown[]) => Promise<unknown> | unknown): Promise<void>;
}

/**
 * DirectMessager - A class for sending and receiving messages directly using RabbitMQ.
 * It allows sending messages to a specific consumer and listening for messages on a queue.
 */

/**
 * Options for DirectMessager.
 */
interface DirectMessagerOptions extends MessagerOptions {
    /** Unique consumer tag for identifying the consumer. */
    consumerTag: string;
}
/**
 * DirectMessager - A class that implements direct messaging using RabbitMQ.
 * It allows sending messages to a specific consumer and listening for messages on a queue.
 */
declare class DirectMessager extends Messager {
    private _consumerTag;
    private _channel;
    constructor(options: DirectMessagerOptions);
    /**
     * The unique consumer tag for identifying the consumer.
     */
    get consumerTag(): string;
    /**
     * Prepare the messager for use by establishing a connection and creating a channel.
     * This method must be called before using the publish or subscribe methods.
     */
    initialize(): Promise<void>;
    /**
     * Send a message to a specific consumer.
     * @param sendTo - The consumer tag or queue name to send the message to.
     * @param message - The message to send.
     * @param ttl - Optional time-to-live for the message in milliseconds.
     * @returns {boolean} - A boolean true if the message was sent successfully, false otherwise.
     * @throws {Error} - Throws an error if the channel is not initialized or if publishing fails.
     */
    send(sendTo: string, message: unknown, ttl?: number): Promise<boolean>;
    /**
     * Listen for messages on the consumer tag.
     * @param callback - A callback function that will be called with the received message.
     * @returns {Promise<void>} - A promise that resolves when the listener is set up.
     * @throws {Error} - Throws an error if the channel is not initialized.
     */
    listen(callback: (message: unknown) => void): Promise<void>;
}

export { DefaultRapidEncoder, DirectMessager, type DirectMessagerOptions, PubSubMessager, type PubSubMessagerOptions, RapidConnector, type RapidConnectorOptions, type RapidEncoder, RpcMessager, type RpcMessagerOptions };
