'use strict';

const globalSlimmerRegistry = require('./global-slimmer-registry-b3bce7e0.js');
const dynamicSlimmer = require('./dynamic-slimmer-f31fdfd7.js');
const misc = require('./misc-3d30df91.js');

/**
 * This decorator is used to re-define a field in a public API controller
 * which will be used for injecting the VegaSlimmer into the public API controller.
 *
 * @returns {Function} The decorator function.
 */
function InjectVegaTelemetrySlimmer() {
  return function (publicAPIPrototype, propertyKey) {
    Object.defineProperty(publicAPIPrototype, propertyKey, {
      set: createSetterForVegaTelemetrySlimmerField(propertyKey),
    });
  };
}
/**
 * Creates a setter for a vega slimmer field which will be used for injecting the VegaSlimmer into the public API controller.
 *
 * @param {string} fieldName The name of the field to inject the VegaSlimmer into.
 * @returns {Function} A setter for the vega slimmer field.
 */
function createSetterForVegaTelemetrySlimmerField(fieldName) {
  return function (slimmer) {
    if (!this[globalSlimmerRegistry.ConsumerSlimmers]) {
      this[globalSlimmerRegistry.ConsumerSlimmers] = new Map();
    }
    this[globalSlimmerRegistry.ConsumerSlimmers].set(fieldName, slimmer);
    slimmer[globalSlimmerRegistry.SlimmerConsumedBy] = this;
    registerSlimMethodsToTelemetry(slimmer, this);
  };
}
/**
 * Given a VegaSlimmer and a public API controller, register the slimmer methods to the public API controller.
 * This is done by appending the slimmer methods to the end of the corresponding public API controller method.
 *
 * @param {VegaSlimmer} slimmer The slimmer to register the slimmer method to.
 * @param {ClassPrototype<unknown>} publicAPIController The public API controller to register the slimmer method to.
 * @returns {void}
 */
function registerSlimMethodsToTelemetry(slimmer, publicAPIController) {
  const slimmerPrototype = slimmer.constructor.prototype;
  if (slimmerPrototype[globalSlimmerRegistry.SlimmerMappedMethod]) {
    for (const entry of slimmerPrototype[globalSlimmerRegistry.SlimmerMappedMethod]) {
      publicAPIController[entry.componentMethodName] = createRegisteredSlimMethod(publicAPIController, slimmer, publicAPIController[entry.componentMethodName], entry);
    }
  }
}
/**
 * Append the slimmer methods to the end of the corresponding public API controller method.
 * Which means the slimmer method will be called when the original method is invoked in the public API controller.
 *
 * @param {ClassPrototype<unknown>} publicAPIController The public API controller to register the slimmer method to.
 * @param {VegaSlimmer} slimmer The slimmer to register the slimmer method to.
 * @param {Function} originalMethod The original method to override.
 * @param {SlimmerMappedMethodEntry} mapEntry The map entry for the slimmer method.
 * @returns {Function} The registered slimmer method.
 */
function createRegisteredSlimMethod(publicAPIController, slimmer, originalMethod, mapEntry) {
  return function (...args) {
    const result = originalMethod.apply(publicAPIController, args);
    slimmer[globalSlimmerRegistry.CurrentlyInvokedMethodName] = mapEntry.componentMethodName;
    if (result && typeof result.then === 'function') {
      result.then(() => slimmer[mapEntry.slimmerMethodName].call(slimmer, ...args));
    }
    else {
      slimmer[mapEntry.slimmerMethodName].call(slimmer, ...args);
    }
    return result;
  };
}

/**
 * The `VegaPublicAPIRuntimeMetricsSlimmerBase` class is used for sending
 * telemetry data to a telemetry service based on defined usage metrics for a public api.
 */
class VegaPublicAPIRuntimeMetricsSlimmerBase extends globalSlimmerRegistry.VegaSlimmer {
  /**
   * The constructor initializes a private readonly property with an optional array of
   * PublicAPIRuntimeMetricsDefinition.
   *
   * @param {string} [className] - The parameter show the class name of public api.
   * @param {PublicAPIRuntimeMetricsDefinition[]} [usageDefinitions] - The `usageDefinitions`
   * parameter in the constructor is of type `PublicAPIRuntimeMetricsDefinition<T>[]`, which is an
   * array of `ComponentUsageRuntimeMetricsDefinition` objects. This parameter is optional as it is
   * marked with a question mark `?`, meaning it can be omitted when creating an instance of
   */
  constructor(className, usageDefinitions) {
    super();
    this.className = className;
    this.usageDefinitions = usageDefinitions;
  }
  /**
   * register the observer when the slimmer method called
   *
   * @param {unknown[]} [args] - The `args` parameter passed in when the slimmer method is executed
   */
  sendPublicAPIPayloadToTelemetry(args) {
    try {
      const publicAPIMetricsPayload = this.getPublicAPIMetricsPayload(args);
      if (publicAPIMetricsPayload) {
        // eslint-disable-next-line spellcheck/spell-checker
        void Promise.resolve().then(function () { return require('./index-3ca118c4.js'); }).then((
        // eslint-disable-next-line spellcheck/spell-checker
        module) => {
          module['default']['emit']('publicAPIMetrics', publicAPIMetricsPayload);
        });
      }
    }
    catch (error) {
      globalSlimmerRegistry.LogUtility.log('Public API payload failed to emit telemetry:', error);
    }
  }
  /**
   * The function `getPublicAPIMetricsPayload` generates a payload containing public api
   * metrics data.
   * The `CurrentlyInvokedMethodName` is a public API method name, it comes from the `InjectVegaTelemetrySlimmer` definition and assignment
   *
   * @param {unknown[]} [args] - The `args` parameter passed in when the slimmer method is executed
   * @returns {Nullable<Record<string, unknown>>} The `getPublicAPIMetricsPayload` method returns an object with the following
   * structure:
   * {
   * 	publicAPIName: string, // The public api name
   * 	data: { // An object containing payload data with keys and boolean values
   * 		'waitForVegaRenderFinished': true,
   * }
   */
  getPublicAPIMetricsPayload(args) {
    const payloadData = {};
    this.usageDefinitions
      .filter((definitions) => definitions.canAccept(this.className, this[globalSlimmerRegistry.CurrentlyInvokedMethodName], args))
      .forEach((definitions) => {
      payloadData[definitions.payloadKey] = definitions.payloadValue(args);
    });
    if (misc.isObjectEmpty(payloadData)) {
      return null;
    }
    return {
      publicAPIName: this.className,
      data: payloadData,
    };
  }
}

/**
 * The function `createComponentUsageRuntimeMetricsSlimmer` creates a slimmer version of a component
 * usage runtime metrics object based on a given class prototype and optional method names to watch.
 *
 * @param {ClassPrototype} mapFromClass - The `mapFromClass` parameter is a ClassPrototype that represents the class
 * from which the dynamic map will be created.
 * @param {MethodKeysOf[]} [watchMethodNames] - The `watchMethodNames` parameter
 * is an optional array of method names that you want to watch for in the component.
 * @param {unknown[]} [initData] - The `initData` parameter allows you to
 * pass an array of initial data to be used during the initialization of the component usage runtime
 * metrics slimmer.
 * @returns {VegaPublicAPIRuntimeMetricsSlimmerBase} The function creates a dynamic public api runtime metrics slimmer.
 */
function createPublicAPIRuntimeMetricsSlimmer(mapFromClass, watchMethodNames, initData) {
  const methodsMap = [];
  watchMethodNames.map((watchMethodName) => methodsMap.push({
    mapFrom: watchMethodName,
    mapTo: 'sendPublicAPIPayloadToTelemetry',
  }));
  return dynamicSlimmer.createDynamicSlimmer(mapFromClass, VegaPublicAPIRuntimeMetricsSlimmerBase, [], methodsMap, [mapFromClass.name, initData]);
}

exports.InjectVegaTelemetrySlimmer = InjectVegaTelemetrySlimmer;
exports.createPublicAPIRuntimeMetricsSlimmer = createPublicAPIRuntimeMetricsSlimmer;
