const AssetMenu = require('./asset.js');
const { TagcodeApp, DynamicAssetMenu } = require('./tagcodeApp.js');
const LoggerMenu = require('./logger.js');
const PhysicalPositionMenu = require('./physicalPosition.js');
const AquonSamplesMenu = require('./aquonSamples.js');
const ConfigManager = require('../configs');

class MenuManager {

    constructor() {
        this.registeredMenus = new Map();
        this.configManager = new ConfigManager('../configs');
        // Register factory functions
        this.registerMenu('asset', (nodeName) => new AssetMenu({
            softwareType: this._getSoftwareType(nodeName)
        })); // static menu to be replaced by dynamic one but later
        //this.registerMenu('asset', (nodeName) => new DynamicAssetMenu(nodeName, new TagcodeApp()));
        this.registerMenu('logger', () => new LoggerMenu());
        this.registerMenu('position', () => new PhysicalPositionMenu());
        this.registerMenu('aquon', () => new AquonSamplesMenu());
    }

    /**
     * Register a menu type with its handler factory function
     * @param {string} menuType - The type of menu (e.g., 'asset', 'logging')
     * @param {function} menuFactory - The menu factory function
     */
    registerMenu(menuType, menuFactory) {
        this.registeredMenus.set(menuType, menuFactory);
    }

    _getSoftwareType(nodeName) {
        if (!nodeName) {
            return null;
        }

        try {
            const config = this.configManager.getConfig(nodeName);
            return config?.functionality?.softwareType || nodeName;
        } catch (error) {
            console.warn(`Unable to determine softwareType for ${nodeName}: ${error.message}`);
            return nodeName;
        }
    }

    /**
     * Create a complete endpoint script with data and initialization functions
     * @param {string} nodeName - The name of the node type
     * @param {Array<string>} menuTypes - Array of menu types to include
     * @returns {string} Complete JavaScript code to serve
     */
    createEndpoint(nodeName, menuTypes) {
        try {
            // ✅ Create instances using factory functions with proper error handling
            const instantiatedMenus = new Map();

            menuTypes.forEach(menuType => {
                try {
                    const factory = this.registeredMenus.get(menuType);
                    if (typeof factory === 'function') {
                        const instance = factory(nodeName);
                        instantiatedMenus.set(menuType, instance);
                    } else {
                        console.warn(`No factory function found for menu type: ${menuType}`);
                    }
                } catch (error) {
                    console.error(`Error creating instance for ${menuType}:`, error);
                }
            });

            // ✅ Collect all menu data with error handling
            const menuData = {};
            menuTypes.forEach(menuType => {
                try {
                    const handler = instantiatedMenus.get(menuType);
                    if (handler && typeof handler.getAllMenuData === 'function') {
                        menuData[menuType] = handler.getAllMenuData(nodeName);
                    } else {
                        // Provide default empty data if method doesn't exist
                        menuData[menuType] = {};
                    }
                } catch (error) {
                    console.error(`Error getting menu data for ${menuType}:`, error);
                    menuData[menuType] = {};
                }
            });

            // ✅ Generate HTML injection code with error handling
            const htmlInjections = menuTypes.map(type => {
                try {
                    const menu = instantiatedMenus.get(type);
                    if (menu && typeof menu.getHtmlInjectionCode === 'function') {
                        return menu.getHtmlInjectionCode(nodeName);
                    }
                    return '';
                } catch (error) {
                    console.error(`Error generating HTML injection for ${type}:`, error);
                    return `// Error generating HTML injection for ${type}: ${error.message}`;
                }
            }).join('\n');

            // ✅ Collect all client initialization code with error handling
            const initFunctions = [];
            menuTypes.forEach(menuType => {
                try {
                    const handler = instantiatedMenus.get(menuType);
                    if (handler && typeof handler.getClientInitCode === 'function') {
                        initFunctions.push(handler.getClientInitCode(nodeName));
                    }
                } catch (error) {
                    console.error(`Error generating init code for ${menuType}:`, error);
                    initFunctions.push(`// Error in ${menuType} initialization: ${error.message}`);
                }
            });

            // Convert menu data to JSON
            const menuDataJSON = JSON.stringify(menuData, null, 2);

            // ✅ Assemble the complete script with comprehensive error handling
            return `
            try {
                // Create the namespace structure with safety checks
                window.EVOLV = window.EVOLV || {};
                window.EVOLV.nodes = window.EVOLV.nodes || {};
                window.EVOLV.nodes.${nodeName} = window.EVOLV.nodes.${nodeName} || {};

                // Initialize menu namespaces
                ${menuTypes.map(type => `window.EVOLV.nodes.${nodeName}.${type}Menu = window.EVOLV.nodes.${nodeName}.${type}Menu || {};`).join('\n                ')}

                // Inject the pre-loaded menu data directly into the namespace
                window.EVOLV.nodes.${nodeName}.menuData = ${menuDataJSON};

                // HTML injections with error handling
                try {
                    ${htmlInjections}
                } catch (htmlError) {
                    console.error('Error in HTML injections for ${nodeName}:', htmlError);
                }

                // Initialize functions with error handling
                try {
                    ${initFunctions.join('\n\n                    ')}
                } catch (initError) {
                    console.error('Error in initialization functions for ${nodeName}:', initError);
                }

                // Main initialization function that calls all menu initializers
                window.EVOLV.nodes.${nodeName}.initEditor = function(node) {
                    try {
                        ${menuTypes.map(type => `
                        try {
                            if (window.EVOLV.nodes.${nodeName}.${type}Menu && window.EVOLV.nodes.${nodeName}.${type}Menu.initEditor) {
                                window.EVOLV.nodes.${nodeName}.${type}Menu.initEditor(node);
                            }
                        } catch (${type}Error) {
                            console.error('Error initializing ${type} menu for ${nodeName}:', ${type}Error);
                        }`).join('')}
                    } catch (editorError) {
                        console.error('Error in main editor initialization for ${nodeName}:', editorError);
                    }
                };

                console.log('${nodeName} menu data and initializers loaded for: ${menuTypes.join(', ')}');
                
            } catch (globalError) {
                console.error('Critical error in ${nodeName} menu initialization:', globalError);
                
                // Fallback initialization
                window.EVOLV = window.EVOLV || {};
                window.EVOLV.nodes = window.EVOLV.nodes || {};
                window.EVOLV.nodes.${nodeName} = window.EVOLV.nodes.${nodeName} || {};
                window.EVOLV.nodes.${nodeName}.initEditor = function(node) {
                    console.warn('Using fallback editor initialization for ${nodeName}');
                };
            }
            `;

        } catch (error) {
            console.error(`Critical error creating endpoint for ${nodeName}:`, error);
            
            // Return minimal fallback script
            return `
            window.EVOLV = window.EVOLV || {};
            window.EVOLV.nodes = window.EVOLV.nodes || {};
            window.EVOLV.nodes.${nodeName} = window.EVOLV.nodes.${nodeName} || {};
            window.EVOLV.nodes.${nodeName}.initEditor = function(node) {
                console.error('Menu system failed to initialize for ${nodeName}');
            };
            console.error('Menu system failed for ${nodeName}:', '${error.message}');
            `;
        }
    }
}

module.exports = MenuManager;
