Source: Router/actionTemplate/helpers.js

/**
 * Helper functions for Action Template operations
 */

/**
 * Recursively extract defaultValue entries from UIaction form field definitions.
 * Returns a flat object mapping field name → defaultValue.
 * 
 * @param {Object} node - A UIaction node (e.g. { "Form.Input": { paras: { name, defaultValue } } })
 * @returns {Object} Map of field name → default value
 */
export const extractUIDefaults = (node) => {
    const defaults = {};
    if (!node || typeof node !== 'object') return defaults;
    for (const [comp, config] of Object.entries(node)) {
        if (config?.paras?.name && config.paras.defaultValue != null) {
            defaults[config.paras.name] = config.paras.defaultValue;
        }
        if (Array.isArray(config?.content)) {
            config.content.forEach(child => Object.assign(defaults, extractUIDefaults(child)));
        }
        if (Array.isArray(config?.children)) {
            config.children.forEach(child => Object.assign(defaults, extractUIDefaults(child)));
        }
    }
    return defaults;
};

/**
 * Recursively builds a translation object by extracting translatable properties 
 * (content, label, placeholder, html) from a serialized input object and its nested 
 * children. Ensures that existing entries in the translation object are not overridden.
 *
 * @param {Object} serialized - The input object containing components and their properties.
 * @param {Object} translateObject - The object to store translations, mapping keys to values.
 *
 * @example
 * const serialized = {
 *   component1: {
 *     paras: { content: "Hello", label: "Greeting", html: "<p>Welcome</p>" },
 *     children: [
 *       { component2: { paras: { content: "World", label: "Planet" } } }
 *     ]
 *   }
 * };
 * const translateObject = {};
 * buildTranslateObject(serialized, translateObject);
 * console.log(translateObject); 
 * // { Hello: "Hello", Greeting: "Greeting", "<p>Welcome</p>": "<p>Welcome</p>", World: "World", Planet: "Planet" }
 */
export const buildTranslateObject = (serialized, translateObject) => {
    const [[component, componentObject]] = Object.entries(serialized);
    if (componentObject.paras) {
        const paras = componentObject.paras;
        if (paras.content)
            translateObject[paras.content] = translateObject[paras.content] ? translateObject[paras.content] : paras.content;   // do not override existing entries
        if (paras.label)
            translateObject[paras.label] = translateObject[paras.label] ? translateObject[paras.label] : paras.label;   // do not override existing entries
        if (paras.placeholder)
            translateObject[paras.placeholder] = translateObject[paras.placeholder] ? translateObject[paras.placeholder] : paras.placeholder;   // do not override existing entries
        if (paras.html)
            translateObject[paras.html] = translateObject[paras.html] ? translateObject[paras.html] : paras.html;   // do not override existing entries
    }
    
    if (componentObject.content) {
        const children = componentObject.content;
        // now call it for further content
        if (Array.isArray(children)) {
            children.forEach((c, Index) => {
                buildTranslateObject(c, translateObject);
            });
        }
        else if (typeof children === 'object')
            buildTranslateObject(children, translateObject);
    }
};

/**
 * Validates input for register endpoint
 * @param {Object} body - Request body
 * @returns {Object} Validation result with success flag and message
 */
export const validateRegisterInput = (body) => {
    const { botUID, organizationUIDs = [], template = {} } = body;

    if (!botUID || !botUID.match(/^UUID\-[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[1-5][0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}/)) {
        return {
            success: false,
            message: 'botUID is required in the UUID format'
        };
    }

    if (!Array.isArray(organizationUIDs) ) {
        return {
            success: false,
            message: 'organizationUIDs must be an array'
        };
    }

    if (organizationUIDs.length === 0) {
        return {
            success: false,
            message: 'organizationUIDs array cannot be empty'
        };
    }

    if (!template || (typeof template === 'object' && Object.keys(template).length === 0)) {
        return {
            success: false,
            message: 'template cannot be empty'
        };
    }

    return { success: true };
};

/**
 * Merges existing template data with new data, preserving customizations
 * @param {Object} templateData - New template data
 * @param {Object} existingData - Existing template data
 * @returns {Object} Merged data
 */
export const mergeTemplateData = (templateData, existingData) => {
    // Guard against undefined inputs
    if (!templateData || !existingData) {
        return templateData || existingData || {};
    }
    
    // Start with new template data
    const Data = { ...templateData };
    
    // Preserve existing translate only (admin customization)
    // name and description come from the bot and should be updated on re-registration
    if (existingData.translate !== undefined) {
        Data.translate = existingData.translate;
    }

    // Merge defaults: preserve admin-entered defaults by overwriting new ones with old ones
    if (templateData.defaults && existingData.defaults) {
        // Start with new defaults, then overwrite with admin customizations
        Object.entries(existingData.defaults).forEach(([key, text]) => {
            Data.defaults[key] = text;
        });
    } else if (existingData.defaults) {
        // No new defaults, keep all existing
        Data.defaults = existingData.defaults;
    }

    return Data;
};