const MenuUtils = require('./menuUtils');

/**
 * Server-side helper for exposing MenuUtils to the browser via HTTP endpoints.
 */
class EndpointUtils {
  /**
   * @param {Object} options
   * @param {Function} options.MenuUtilsClass  the MenuUtils constructor/function
   */
  constructor({ MenuUtilsClass = MenuUtils } = {}) {
    this.MenuUtils = MenuUtilsClass;
  }

  /**
   * Registers an HTTP GET endpoint that serves the client-side MenuUtils code
   * @param {object} RED          the Node-RED API object
   * @param {string} nodeName     the name of the node (used in the URL)
   * @param {object} customHelpers  additional helper functions to inject
   */
  createMenuUtilsEndpoint(RED, nodeName, customHelpers = {}) {
    RED.httpAdmin.get(`/${nodeName}/resources/menuUtils.js`, (req, res) => {
      console.log(`Serving menuUtils.js for ${nodeName} node`);
      res.set('Content-Type', 'application/javascript');
      const browserCode = this.generateMenuUtilsCode(nodeName, customHelpers);
      res.send(browserCode);
    });
  }

  /**
   * Generates the browser-side JavaScript that redefines MenuUtils and helper fns
   * @param {string} nodeName
   * @param {object} customHelpers  map of name: functionString pairs
   * @returns {string}  a JS snippet to run in the browser
   */
  generateMenuUtilsCode(nodeName, customHelpers = {}) {
    // Default helper implementations to expose alongside MenuUtils
    const defaultHelpers = {
      validateRequired: `function(value) {
        return value != null && value.toString().trim() !== '';
      }`,
      formatDisplayValue: `function(value, unit) {
        return \`${'${'}value} ${'${'}unit || ''}\`.trim();
      }`,
      validateScaling: `function(min, max) {
        return !isNaN(min) && !isNaN(max) && Number(min) < Number(max);
      }`,
      validateUnit: `function(unit) {
        return typeof unit === 'string' && unit.trim() !== '';
      }`,
    };

    // Merge any custom overrides
    const allHelpers = { ...defaultHelpers, ...customHelpers };

    // Build the helpers code block
    const helpersCode = Object.entries(allHelpers)
      .map(([name, fnBody]) => `        ${name}: ${fnBody}`)
      .join(',\n');

    // Introspect MenuUtils prototype to extract method definitions
    const proto = this.MenuUtils.prototype;
    const browserMethods = Object.getOwnPropertyNames(proto)
      .filter(key => key !== 'constructor')
      .map(methodName => {
        const fn = proto[methodName];
        const src = fn.toString();
        const isAsync = fn.constructor.name === 'AsyncFunction';
        // extract signature and body
        const signature = src.slice(src.indexOf('('));
        const prefix = isAsync ? 'async ' : '';
        return `  ${prefix}${methodName}${signature}`;
      })
      .join('\n\n');

    // Return a complete JS snippet for the browser
    return `
// Auto-generated MenuUtils for node: ${nodeName}
window.EVOLV = window.EVOLV || {};
window.EVOLV.nodes = window.EVOLV.nodes || {};
window.EVOLV.nodes.${nodeName} = window.EVOLV.nodes.${nodeName} || {};

class MenuUtils {
  constructor(opts) {
    // Allow same options API as server-side
    this.useCloud = opts.useCloud || false;
    this.projectSettings = opts.projectSettings || {};
    // any other client-side initialization...
  }

${browserMethods}
}

window.EVOLV.nodes.${nodeName}.utils = {
  menuUtils: new MenuUtils({}),
  helpers: {
${helpersCode}
  }
};

console.log('Loaded EVOLV.nodes.${nodeName}.utils.menuUtils');
`;
  }
}

module.exports = EndpointUtils;
