import { ScriptEventCommandMessageAfterEvent, system } from "@minecraft/server";
import { config } from "../config";

/**
 * A manager class for handling custom script events
 * This class allows registering, retrieving, and removing event handlers
 */
class ScriptEventManager {
    constructor() {
        /**
         * @private
         * @type {Map<string, function(ScriptEventCommandMessageAfterEvent): void>}
         * A map that stores event names and their corresponding callback functions.
         */
        this._scriptEvents = new Map();

        /** @private */
        this._namespace = config.namespace;

        // Initialize event listeners
        this._initEvents();
    }

    /**
     * Initializes the system event listener for receiving script events.
     * @private
     */
    _initEvents() {
        system.afterEvents.scriptEventReceive.subscribe(
            /**
             * @param {ScriptEventCommandMessageAfterEvent} event - The event data received.
             */
            (event) => this._onScriptEventReceive(event)
        );
    }

    /**
     * Handles the received script event and triggers the corresponding callback.
     * @private
     * @param {ScriptEventCommandMessageAfterEvent} eventData - The event data received from the game.
     */
    _onScriptEventReceive(eventData) {
        const eventName = eventData.id;
        if (eventName.split(':')[0] !== this._namespace) return;
        if (this._scriptEvents.has(eventName)) {
            this._scriptEvents.get(eventName)(eventData);
        } else {
            if (config.debug) console.warn(
                `@ScriptEventManager.triggerEvent\nEvent: "${eventName}" does not exist:\n`,
                [...this._scriptEvents.keys()].join("\n")
            );
        }
    }

    /**
     * @param {string} eventName
     * @returns {boolean} - returns whether an event with the specified event name exists or not. 
     */
    has(eventName) {
        return this._scriptEvents.has(eventName);
    }

    /**
     * Retrieves a registered handler for a specific script event.
     * @param {string} eventName - The name of the script event without namespace.
     * @returns {function(ScriptEventCommandMessageAfterEvent): void | undefined} 
     * The callback function registered for the event, or `undefined` if the event is not registered.
     */
    get(eventName) {
        return this._scriptEvents.get(`${this._namespace}:${eventName}`);
    }

    /**
     * Registers a handler for a specific script event.
     * @param {string} eventName - The name of the script event without namespace.
     * @param {function(ScriptEventCommandMessageAfterEvent): void} callback - The function to run when the script event is triggered.
     */
    registerEvent(eventName, callback) {
        this._scriptEvents.set(`${this._namespace}:${eventName}`, callback);
    }

    /**
     * Removes a handler for a specific script event.
     * @param {string} eventName - The name of the script event without namespace.
     */
    removeEvent(eventName) {
        if (this._scriptEvents.has(`${this._namespace}:${eventName}`)) {
            this._scriptEvents.delete(`${this._namespace}:${eventName}`);
        }
    }
}

export const scriptEventManager = new ScriptEventManager(); 
