import EventEmitter, { on } from "node:events";
import type { ChatsChatid } from "~/schemas/public/Chats";
import type { MessagesMessageid } from "~/schemas/public/Messages";
import type { UsersId } from "~/schemas/public/Users";
import type { ChatMessage } from "~/server/services/messages.service";

export type WhoIsTyping = {
	userId: UsersId;
	username: string;
};

export type OnlineStatus = {
	userId: UsersId;
	nome: string;
	isAdmin: boolean;
};

type ChatUpdate =
	| {
			eventType: "online";
			data: OnlineStatus[];
	  }
	| {
			eventType: "typing";
			data: WhoIsTyping[];
	  };

// export type AddEvent = {
//   eventType: "add";
//   data: ChatMessage;
// };

type EditEvent = {
	eventType: "edit";
	data: { messageId: MessagesMessageid; message: string };
};

type DeleteEvent = {
	eventType: "delete";
	data: MessagesMessageid;
};

export type MessageUpdate = EditEvent | DeleteEvent;

interface Events {
	add: [chatId: ChatsChatid, data: ChatMessage];

	test: [ch: string, data: string];
	notificaUpdate: [userId: UsersId];
	messageUpdates: [chatId: ChatsChatid, data: MessageUpdate];
	chatUpdates: [chatId: ChatsChatid, data: ChatUpdate];
}

enum LogLevel {
	NONE = 0, // No logs
	ERROR = 1, // Only errors
	WARN = 2, // Warnings and errors
	INFO = 3, // Info, warnings, and errors
	DEBUG = 4, // Dbg, info, warnings, and errors
}

class IterableEventEmitter extends EventEmitter<Events> {
	private logLevel: LogLevel = LogLevel.INFO; // Default log level

	// Method to set the log level
	setLogLevel(level: LogLevel): void {
		this.logLevel = level;
	}

	// Method to log messages based on the current log level
	private log(level: LogLevel, message: string, ...args: unknown[]): void {
		if (this.logLevel >= level) {
			console.log(message, ...args);
		}
	}

	// Method to log emitted events
	private logEvent(eventName: string | symbol, args: unknown[]): void {
		this.log(
			LogLevel.DEBUG,
			`[EventEmitter] Event emitted: ${String(eventName)}`,
			args,
		);
	}

	// Method to log added listeners
	private logListener(eventName: string | symbol): void {
		this.log(
			LogLevel.INFO,
			`[EventEmitter] Listener added for event: ${String(eventName)}`,
		);
	}

	// Method to log removed listeners
	private logRemoveListener(eventName: string | symbol): void {
		this.log(
			LogLevel.INFO,
			`[EventEmitter] Listener removed for event: ${String(eventName)}`,
		);
	}

	// Override the `emit` method to include logging
	emit<TEventName extends keyof Events>(
		eventName: TEventName,
		...args: Events[TEventName]
	): boolean {
		this.logEvent(eventName, args); // Log the event
		//@ts-expect-error
		return super.emit(eventName, ...args); // Call the original `emit` method
	}

	// Override the `on` method to include logging
	on<TEventName extends keyof Events>(
		eventName: TEventName,
		listener: (...args: Events[TEventName]) => void,
	): this {
		this.logListener(eventName); // Log the listener addition
		//@ts-expect-error
		return super.on(eventName, listener); // Call the original `on` method
	}

	// Override the `off` method to include logging
	off<TEventName extends keyof Events>(
		eventName: TEventName,
		listener: (...args: Events[TEventName]) => void,
	): this {
		this.logRemoveListener(eventName); // Log the listener removal
		//@ts-expect-error
		return super.off(eventName, listener); // Call the original `off` method
	}

	toIterable<TEventName extends keyof Events>(
		eventName: TEventName,
		opts?: NonNullable<Parameters<typeof on>[2]>,
	): AsyncIterable<Events[TEventName]> {
		this.log(
			LogLevel.DEBUG,
			`[EventEmitter] Creating async iterable for event: ${String(eventName)}`,
		);
		return on(this, eventName, opts) as AsyncIterable<Events[TEventName]>;
	}
}

// In a real app, you'd probably use Redis or something
export const ee = new IterableEventEmitter();

// Example: Set log level to DEBUG
ee.setLogLevel(LogLevel.ERROR);

ee.setMaxListeners(200);
