/*
 * @moleculer/channels
 * Copyright (c) 2025 MoleculerJS (https://github.com/moleculerjs/channels)
 * MIT Licensed
 */

declare module "@moleculer/channels" {
	import {
		ServiceBroker,
		Context,
		Service,
		Middleware,
		Logger,
		Serializers
	} from "moleculer";

	// === Core Interfaces ===

	/**
	 * Dead-letter-queue options
	 */
	export interface DeadLetteringOptions {
		/** Enable dead-letter-queue */
		enabled: boolean;
		/** Name of the dead-letter queue */
		queueName?: string;
		/** Name of the dead-letter exchange (only for AMQP adapter) */
		exchangeName?: string;
		/** Options for the dead-letter exchange (only for AMQP adapter) */
		exchangeOptions?: Record<string, any>;
		/** Options for the dead-letter queue (only for AMQP adapter) */
		queueOptions?: Record<string, any>;
		/** Custom function to convert Error object to header entries */
		transformErrorToHeaders?: (error: Error) => Record<string, string>;
		/** Custom function to parse error info from headers back to data */
		transformHeadersToErrorData?: (headers: Record<string, string>) => Record<string, any>;
		/** Time-to-live in seconds for error info storage (only for Redis adapter) */
		errorInfoTTL?: number;
	}

	/**
	 * Base consumer configuration
	 */
	export interface Channel<TThis = Service> {
		/** Consumer ID */
		id?: string;
		/** Channel/Queue/Stream name */
		name?: string;
		/** Consumer group name */
		group?: string;
		/** Create Moleculer Context */
		context?: boolean;
		/** Flag denoting if service is stopping */
		unsubscribing?: boolean;
		/** Maximum number of messages that can be processed simultaneously */
		maxInFlight?: number;
		/** Maximum number of retries before sending the message to dead-letter-queue */
		maxRetries?: number;
		/** Dead-letter-queue options */
		deadLettering?: DeadLetteringOptions;
		/** User defined handler */
		handler?: (this: TThis, payload: any, raw?: any) => Promise<any> | any;
		/** Tracing options */
		tracing?: boolean | TracingOptions;
	}

	/**
	 * Channel registry entry
	 */
	export interface ChannelRegistryEntry {
		/** Service instance class */
		svc: Service;
		/** Channel name */
		name: string;
		/** Channel object */
		chan: Channel;
	}

	/**
	 * Adapter configuration object
	 */
	export interface AdapterConfig {
		/** Adapter name */
		type: string;
		/** Adapter options */
		options?: Record<string, any>;
	}

	/**
	 * Middleware options
	 */
	export interface MiddlewareOptions {
		/** Adapter name, connection string, or configuration object */
		adapter: string | AdapterConfig;
		/** Property name of channels definition in service schema */
		schemaProperty?: string;
		/** Method name to send messages */
		sendMethodName?: string;
		/** Property name of the adapter instance in broker instance */
		adapterPropertyName?: string;
		/** Method name to add to service in order to trigger channel handlers */
		channelHandlerTrigger?: string;
		/** Using Moleculer context in channel handlers by default */
		context?: boolean;
	}

	/**
	 * Tracing options
	 */
	export interface TracingOptions {
		/** Enable/disable tracing */
		enabled?: boolean;
		/** Custom span name */
		spanName?: string | ((ctx: Context<any, any>) => string);
		/** Tags for tracing */
		tags?: Record<string, any> | ((ctx: Context<any, any>) => Record<string, any>);
		/** Enable safety tags */
		safetyTags?: boolean;
	}

	/**
	 * Send options for publishing messages
	 */
	export interface SendOptions {
		/** Headers to include with message */
		headers?: Record<string, any>;
		/** Moleculer context to propagate */
		ctx?: Context<any, any>;
		/** Additional adapter-specific options */
		[key: string]: any;
	}

	// === Base Adapter ===

	/**
	 * Base adapter default options
	 */
	export interface BaseDefaultOptions {
		/** Adapter prefix */
		prefix?: string;
		/** Name of the consumer */
		consumerName?: string;
		/** Type of serializer to use in message exchange */
		serializer?: string;
		/** Maximum number of retries before sending the message to dead-letter-queue or drop */
		maxRetries?: number;
		/** Maximum number of messages that can be processed in parallel */
		maxInFlight?: number;
		/** Dead-letter-queue options */
		deadLettering?: DeadLetteringOptions;
	}

	/**
	 * Base adapter class
	 */
	export class BaseAdapter {
		/** Adapter options */
		opts: BaseDefaultOptions;
		/** Service broker instance */
		broker: ServiceBroker;
		/** Logger instance */
		logger: Logger;
		/** Promise constructor */
		Promise: typeof Promise;
		/** Serializer instance */
		serializer: Serializers.Base;
		/** Active messages tracking */
		activeMessages: Map<string, Array<string | number>>;
		/** Connection status */
		connected: boolean;

		/** Function to convert Error to header entries for dead-lettering */
		transformErrorToHeaders: (error: Error) => Record<string, string> | null;
		/** Function to parse error info from headers */
		transformHeadersToErrorData: (headers: Record<string, string>) => Record<string, any> | null;

		constructor(opts?: BaseDefaultOptions);

		/**
		 * Initialize the adapter
		 */
		init(broker: ServiceBroker, logger: Logger): void;

		/**
		 * Register adapter related metrics
		 */
		registerAdapterMetrics(broker: ServiceBroker): void;

		/**
		 * Increment metrics
		 */
		metricsIncrement(metricName: string, chan: Channel): void;

		/**
		 * Check client library version
		 */
		checkClientLibVersion(library: string, requiredVersions: string): boolean;

		/**
		 * Initialize active messages tracking for a channel
		 */
		initChannelActiveMessages(channelID: string, toThrow?: boolean): void;

		/**
		 * Stop active messages tracking for a channel
		 */
		stopChannelActiveMessages(channelID: string): void;

		/**
		 * Add active message IDs
		 */
		addChannelActiveMessages(channelID: string, IDs: Array<string | number>): void;

		/**
		 * Remove active message IDs
		 */
		removeChannelActiveMessages(channelID: string, IDs: Array<string | number>): void;

		/**
		 * Get number of active messages for a channel
		 */
		getNumberOfChannelActiveMessages(channelID: string): number;

		/**
		 * Get number of tracked channels
		 */
		getNumberOfTrackedChannels(): number;

		/**
		 * Add prefix to topic name
		 */
		addPrefixTopic(topicName: string): string;

		/**
		 * Connect to the adapter
		 */
		connect(): Promise<void>;

		/**
		 * Disconnect from adapter
		 */
		disconnect(): Promise<void>;

		/**
		 * Subscribe to a channel
		 */
		subscribe(chan: Channel, svc: Service): Promise<void>;

		/**
		 * Unsubscribe from a channel
		 */
		unsubscribe(chan: Channel): Promise<void>;

		/**
		 * Publish a payload to a channel
		 */
		publish(channelName: string, payload: any, opts?: SendOptions): Promise<void>;

		/**
		 * Parse message headers from raw message
		 */
		parseMessageHeaders(raw: any): Record<string, any> | null;
	}

	// === Adapter-specific options ===

	/**
	 * Redis adapter options
	 */
	export interface RedisDefaultOptions extends BaseDefaultOptions {
		redis?: {
			/** Redis connection URL */
			url?: string;
			/** Consumer options */
			consumerOptions?: {
				/** Read timeout interval in milliseconds */
				readTimeoutInterval?: number;
				/** Min idle time for claiming pending messages */
				minIdleTime?: number;
				/** Claim interval in milliseconds */
				claimInterval?: number;
				/** Starting ID for consumer group */
				startID?: string;
				/** Processing attempts interval */
				processingAttemptsInterval?: number;
			};
			/** Additional Redis client options */
			[key: string]: any;
		};
	}

	/**
	 * AMQP adapter options
	 */
	export interface AmqpDefaultOptions extends BaseDefaultOptions {
		amqp?: {
			/** AMQP connection URL(s) */
			url?: string | string[];
			/** Socket options */
			socketOptions?: Record<string, any>;
			/** Queue options */
			queueOptions?: Record<string, any>;
			/** Exchange options */
			exchangeOptions?: Record<string, any>;
			/** Message options */
			messageOptions?: Record<string, any>;
			/** Consumer options */
			consumerOptions?: Record<string, any>;
			/** Publish assert exchange options */
			publishAssertExchange?: {
				enabled?: boolean;
				exchangeOptions?: Record<string, any>;
			};
		};
	}

	/**
	 * Kafka adapter options
	 */
	export interface KafkaDefaultOptions extends BaseDefaultOptions {
		kafka?: {
			/** Kafka brokers */
			brokers?: string[];
			/** Log creator function */
			logCreator?: () => (logEntry: any) => void;
			/** Producer options */
			producerOptions?: Record<string, any>;
			/** Consumer options */
			consumerOptions?: Record<string, any>;
			/** Additional Kafka client config */
			[key: string]: any;
		};
	}

	/**
	 * NATS adapter options
	 */
	export interface NatsDefaultOptions extends BaseDefaultOptions {
		/** NATS URL */
		url?: string;
		nats?: {
			/** Connection options */
			connectionOptions?: Record<string, any>;
			/** Stream configuration */
			streamConfig?: Record<string, any>;
			/** Consumer options */
			consumerOptions?: {
				mack?: boolean;
				config?: {
					deliver_policy?: string;
					ack_policy?: string;
					max_ack_pending?: number;
				};
			};
		};
	}

	/**
	 * Fake adapter options (for testing)
	 */
	export interface FakeOptions extends BaseDefaultOptions {
		/** Service prefix */
		servicePrefix?: string;
		/** Event prefix */
		eventPrefix?: string;
	}

	// === Adapter Classes ===

	/**
	 * Redis adapter
	 */
	export class RedisAdapter extends BaseAdapter {
		constructor(opts?: RedisDefaultOptions | string);
	}

	/**
	 * AMQP adapter
	 */
	export class AmqpAdapter extends BaseAdapter {
		constructor(opts?: AmqpDefaultOptions | string);
	}

	/**
	 * Kafka adapter
	 */
	export class KafkaAdapter extends BaseAdapter {
		constructor(opts?: KafkaDefaultOptions | string);
	}

	/**
	 * NATS adapter
	 */
	export class NatsAdapter extends BaseAdapter {
		constructor(opts?: NatsDefaultOptions | string);
	}

	/**
	 * Fake adapter (for testing)
	 */
	export class FakeAdapter extends BaseAdapter {
		constructor(opts?: FakeOptions | string);
	}

	// === Adapters Registry ===
	export interface AdaptersRegistry {
		Base: typeof BaseAdapter;
		AMQP: typeof AmqpAdapter;
		Fake: typeof FakeAdapter;
		Kafka: typeof KafkaAdapter;
		NATS: typeof NatsAdapter;
		Redis: typeof RedisAdapter;
		resolve(opt: string | AdapterConfig | BaseAdapter): BaseAdapter;
		register(name: string, value: typeof BaseAdapter): void;
	}

	// === Constants ===
	export interface Constants {
		/** Number of redelivery attempts */
		HEADER_REDELIVERED_COUNT: string;
		/** Consumer group name */
		HEADER_GROUP: string;
		/** Name of the channel where an error occurred while processing the message */
		HEADER_ORIGINAL_CHANNEL: string;
		/** Name of consumer group that could not process the message properly */
		HEADER_ORIGINAL_GROUP: string;

		/** Prefix for error-related headers */
		HEADER_ERROR_PREFIX: string;
		/** Error message */
		HEADER_ERROR_MESSAGE: string;
		/** Error code */
		HEADER_ERROR_CODE: string;
		/** Error stack trace */
		HEADER_ERROR_STACK: string;
		/** Error type */
		HEADER_ERROR_TYPE: string;
		/** Error data */
		HEADER_ERROR_DATA: string;
		/** Error name */
		HEADER_ERROR_NAME: string;
		/** Error retryable flag */
		HEADER_ERROR_RETRYABLE: string;
		/** Timestamp when the error happened */
		HEADER_ERROR_TIMESTAMP: string;

		/** Metrics constants */
		METRIC_CHANNELS_MESSAGES_SENT: string;
		METRIC_CHANNELS_MESSAGES_TOTAL: string;
		METRIC_CHANNELS_MESSAGES_ACTIVE: string;
		METRIC_CHANNELS_MESSAGES_TIME: string;
		METRIC_CHANNELS_MESSAGES_ERRORS_TOTAL: string;
		METRIC_CHANNELS_MESSAGES_RETRIES_TOTAL: string;
		METRIC_CHANNELS_MESSAGES_DEAD_LETTERING_TOTAL: string;

		/** Thrown when incoming messages cannot be deserialized */
		INVALID_MESSAGE_SERIALIZATION_ERROR_CODE: string;
	}

	// === Enhanced ServiceBroker interface ===
	export interface EnhancedServiceBroker extends ServiceBroker {
		/**
		 * Send message to channel (added by channels middleware)
		 */
		sendToChannel(channelName: string, payload: any, opts?: SendOptions): Promise<void>;

		/**
		 * Channel adapter instance (added by channels middleware)
		 */
		channelAdapter: BaseAdapter;
	}

	// === Enhanced Service interface ===
	export interface EnhancedService extends Service {
		/**
		 * Emit local channel handler (added by channels middleware for testing)
		 */
		emitLocalChannelHandler(channelName: string, payload: any, raw?: any): Promise<any>;
	}

	declare module "moleculer" {
		export interface ServiceBroker {
			sendToChannel(channelName: string, payload: any, opts?: SendOptions): Promise<void>;
		}

		export interface ServiceSchema<TSettings = ServiceSettingSchema,
		TMethods = Record<string, any>,
		TVars = Record<string, any>,
		TThis = Service<TSettings> & TVars & TMethods> {
			/**
			 * Channel definitions for the service
			 */
			channels?: {
				[channelName: string]:
					| Channel<TThis>
					| ((this: TThis, payload: any, raw?: any) => Promise<any> | any);
			};
		}
	}

	// === Main Module Exports ===

	/**
	 * Create Channels middleware
	 */
	declare function ChannelsMiddleware(opts: MiddlewareOptions): Middleware & {
		name: string;
		created(broker: ServiceBroker): void;
		serviceCreated(svc: Service): Promise<void>;
		serviceStopping(svc: Service): Promise<void>;
		starting(): Promise<void>;
		stopped(): Promise<void>;
	};

	/**
	 * Tracing middleware factory
	 */
	declare function TracingMiddleware(): Middleware;

	export {
		ChannelsMiddleware as Middleware,
		TracingMiddleware as Tracing,
		AdaptersRegistry as Adapters
	}
}
