import amqp from 'amqplib';

interface ExchangeParams {
    name: string;
    type: 'direct' | 'fanout' | 'topic' | 'headers';
    options?: amqp.Options.AssertExchange;
}
interface QueueParams {
    name?: string;
    options?: amqp.Options.AssertQueue;
    /**
     * Maximum supported priority
     * @default 10
     */
    maxPriority?: number;
    bindings: {
        exchange: string;
        routingKey?: string;
        headers?: Record<string, any>;
    }[];
    deadLetter?: {
        exchange: string;
        routingKey?: string;
    };
}
interface RabbitMQConfig {
    connection: {
        url: string;
        /**
         * @default 5000
         */
        reconnectInterval?: number;
        /**
         * @default 5
         */
        maxRetries?: number;
    };
    exchanges: ExchangeParams[];
    queues: QueueParams[];
    channel?: {
        prefetchCount?: number;
    };
}
interface Message {
    routingKey: string;
    date: string;
    entityTypeId?: number;
    entityId?: number;
    retryCount?: number;
    additionalData?: Record<string, any>;
    [key: string]: any;
}
interface MessageOptions extends amqp.Options.Publish {
    /**
     * Message priority
     * @min:0
     * @max:10
     *
     * @default 5
     */
    priority?: number;
    headers?: Record<string, any>;
    [key: string]: any;
}
type MessageHandler<T = any> = (msg: T, ack: () => void, nack: () => void) => Promise<void>;

declare abstract class RabbitMQBase {
    protected connection: amqp.ChannelModel;
    protected channel: amqp.Channel;
    protected config: RabbitMQConfig;
    constructor(config: RabbitMQConfig);
    connect(): Promise<void>;
    /**
     * Initialize all exchanges from the config
     * @protected
     */
    protected setupExchanges(): Promise<void>;
    registerExchange(exchange: ExchangeParams): Promise<void>;
    /**
     * Initialize queues from config
     * @protected
     */
    protected setupQueues(): Promise<void>;
    registerQueue(queue: QueueParams): Promise<amqp.Replies.AssertQueue>;
    disconnect(): Promise<void>;
}

declare class RabbitMQProducer extends RabbitMQBase {
    private exchanges;
    initialize(): Promise<void>;
    connect(): Promise<void>;
    registerExchange(exchange: ExchangeParams): Promise<void>;
    publish<T>(exchangeName: string, routingKey: string, message: T, options?: MessageOptions): Promise<boolean>;
}

declare class RabbitMQConsumer extends RabbitMQBase {
    private retries;
    private handlers;
    initialize(): Promise<void>;
    connect(): Promise<void>;
    private handleReconnect;
    registerHandler<T>(queueName: string, handler: MessageHandler<T>): void;
    unRegisterHandler(queueName: string): void;
    consume(queueName: string): Promise<amqp.Replies.Consume>;
}

declare class RabbitRPC {
    private producer;
    private consumer;
    constructor(producer: RabbitMQProducer, consumer: RabbitMQConsumer);
    call<T>(exchange: string, routingKey: string, payload: any, timeout?: number): Promise<T>;
}

export { RabbitMQBase, RabbitMQConsumer, RabbitMQProducer, RabbitRPC };
export type { ExchangeParams, Message, MessageHandler, MessageOptions, QueueParams, RabbitMQConfig };
