// type EventCallback = (...args: any[]) => void;

// export class myEventEmitter {
//     private events: Map<string, Set<EventCallback>>;

//     constructor() {
//         this.events = new Map();
//     }

//     public on(eventName: string, callback: EventCallback) {
//         if (!this.events.has(eventName)) {
//             this.events.set(eventName, new Set());
//         }

//         const eventListeners = this.events.get(eventName);
//         eventListeners.add(callback);
//     }

//     public once(eventName: string, callback: EventCallback) {
//         const onceCallback: EventCallback = (...args: any[]) => {
//             this.off(eventName, onceCallback);
//             callback(...args);
//         };
//         this.on(eventName, onceCallback);
//     }

//     public off(eventName: string, callback: EventCallback) {
//         if (!this.events.has(eventName)) {
//             return;
//         }

//         const eventListeners = this.events.get(eventName);
//         let isSucc = eventListeners.delete(callback);

//         if (eventListeners.size === 0) {
//             this.events.delete(eventName);
//         }
//     }

//     public emit(eventName: string, ...args: any[]) {
//         if (!this.events.has(eventName)) {
//             return;
//         }

//         const eventListeners = this.events.get(eventName);
//         eventListeners.forEach((callback) => {
//             callback(...args);
//         });
//     }
// }


type Listener<T extends any[]> = (...args: T) => void;

interface EventMap {
    [event: string]: unknown[];
}

interface ListenerMap {
    [event: string]: Listener<any>[];
}

interface EventEmitter {
    on<T extends any[]>(event: string, listener: Listener<T>): this;
    once<T extends any[]>(event: string, listener: Listener<T>): this;
    off<T extends any[]>(event: string, listener: Listener<T>): this;
    emit<T extends any[]>(event: string, ...args: T): this;
}

class EventEmitterImpl implements EventEmitter {
    private events: EventMap = {};
    private listeners: ListenerMap = {};

    public on<T extends any[]>(event: string, listener: Listener<T>): this {
        this.addListener(event, listener);
        return this;
    }

    public once<T extends any[]>(event: string, listener: Listener<T>): this {
        const onceListener: Listener<T> = (...args: T) => {
            this.off(event, onceListener);
            listener(...args);
        };
        this.addListener(event, onceListener);
        return this;
    }

    public off<T extends any[]>(event: string, listener: Listener<T>): this {
        const listeners = this.listeners[event];
        if (!listeners) return this;
        const index = listeners.indexOf(listener);
        if (index >= 0) {
            listeners.splice(index, 1);
            if (listeners.length === 0) {
                delete this.events[event];
                delete this.listeners[event];
            }
        }
        return this;
    }

    public emit<T extends any[]>(event: string, ...args: T): this {
        const listeners = this.listeners[event];
        if (!listeners) return this;
        const events = this.events[event] as T;
        if (events) {
            events.push(...args);
        } else {
            this.events[event] = args;
        }
        listeners.slice().forEach((listener) => listener(...args));
        return this;
    }

    public clear(): void {
        this.events = {};
        this.listeners = {};
    }

    private addListener<T extends any[]>(event: string, listener: Listener<T>): void {
        if (!this.listeners[event]) {
            this.listeners[event] = [];
        }
        this.listeners[event].push(listener);
    }
}

export const myEventEmitter = EventEmitterImpl;

