/* This is a copy of the regular `BootstrapHandler.js` in the flow-server
   module, but with the following modifications:
   - The main function is exported as an ES module for lazy initialization.
   - Application configuration is passed as a parameter instead of using
     replacement placeholders as in the regular bootstrapping.
   - It reuses `Vaadin.Flow.clients` if exists.
   - Fixed lint errors.
 */
const init = function (appInitResponse) {
  window.Vaadin = window.Vaadin || {};
  window.Vaadin.Flow = window.Vaadin.Flow || {};

  var apps = {};
  var widgetsets = {};

  var log;
  if (typeof window.console === undefined || !window.location.search.match(/[&?]debug(&|$)/)) {
    /* If no console.log present, just use a no-op */
    log = function () {};
  } else if (typeof window.console.log === 'function') {
    /* If it's a function, use it with apply */
    log = function () {
      window.console.log.apply(window.console, arguments);
    };
  } else {
    /* In IE, its a native function for which apply is not defined, but it works
     without a proper 'this' reference */
    log = window.console.log;
  }

  var isInitializedInDom = function (appId) {
    var appDiv = document.getElementById(appId);
    if (!appDiv) {
      return false;
    }
    for (var i = 0; i < appDiv.childElementCount; i++) {
      var className = appDiv.childNodes[i].className;
      /* If the app div contains a child with the class
      'v-app-loading' we have only received the HTML
      but not yet started the widget set
      (UIConnector removes the v-app-loading div). */
      if (className && className.indexOf('v-app-loading') != -1) {
        return false;
      }
    }
    return true;
  };

  /*
   * Needed for Testbench compatibility, but prevents any Vaadin 7 app from
   * bootstrapping unless the legacy vaadinBootstrap.js file is loaded before
   * this script.
   */
  window.Vaadin = window.Vaadin || {};
  window.Vaadin.Flow = window.Vaadin.Flow || {};

  /*
   * Needed for wrapping custom javascript functionality in the components (i.e. connectors)
   */
  window.Vaadin.Flow.tryCatchWrapper = function (originalFunction, component) {
    return function () {
      try {
        // eslint-disable-next-line
        const result = originalFunction.apply(this, arguments);
        return result;
      } catch (error) {
        console.error(
          `There seems to be an error in ${component}:
${error.message}
Please submit an issue to https://github.com/vaadin/flow-components/issues/new/choose`
        );
      }
    };
  };

  if (!window.Vaadin.Flow.initApplication) {
    window.Vaadin.Flow.clients = window.Vaadin.Flow.clients || {};

    window.Vaadin.Flow.initApplication = function (appId, config) {
      var testbenchId = appId.replace(/-\d+$/, '');

      if (apps[appId]) {
        if (
          window.Vaadin &&
          window.Vaadin.Flow &&
          window.Vaadin.Flow.clients &&
          window.Vaadin.Flow.clients[testbenchId] &&
          window.Vaadin.Flow.clients[testbenchId].initializing
        ) {
          throw new Error('Application ' + appId + ' is already being initialized');
        }
        if (isInitializedInDom(appId)) {
          if (appInitResponse.appConfig.productionMode) {
            throw new Error('Application ' + appId + ' already initialized');
          }

          // Remove old contents for Flow
          var appDiv = document.getElementById(appId);
          for (var i = 0; i < appDiv.childElementCount; i++) {
            appDiv.childNodes[i].remove();
          }

          // For devMode reset app config and restart widgetset as client
          // is up and running after hrm update.
          const getConfig = function (name) {
            return config[name];
          };

          /* Export public data */
          const app = {
            getConfig: getConfig
          };
          apps[appId] = app;

          if (widgetsets['client'].callback) {
            log('Starting from bootstrap', appId);
            widgetsets['client'].callback(appId);
          } else {
            log('Setting pending startup', appId);
            widgetsets['client'].pendingApps.push(appId);
          }
          return apps[appId];
        }
      }

      log('init application', appId, config);

      window.Vaadin.Flow.clients[testbenchId] = {
        isActive: function () {
          return true;
        },
        initializing: true,
        productionMode: mode
      };

      var getConfig = function (name) {
        var value = config[name];
        return value;
      };

      /* Export public data */
      var app = {
        getConfig: getConfig
      };
      apps[appId] = app;

      var widgetset = 'client';
      widgetsets[widgetset] = {
        pendingApps: []
      };
      if (widgetsets[widgetset].callback) {
        log('Starting from bootstrap', appId);
        widgetsets[widgetset].callback(appId);
      } else {
        log('Setting pending startup', appId);
        widgetsets[widgetset].pendingApps.push(appId);
      }

      return app;
    };
    window.Vaadin.Flow.getAppIds = function () {
      var ids = [];
      for (var id in apps) {
        if (Object.prototype.hasOwnProperty.call(apps, id)) {
          ids.push(id);
        }
      }
      return ids;
    };
    window.Vaadin.Flow.getApp = function (appId) {
      return apps[appId];
    };
    window.Vaadin.Flow.registerWidgetset = function (widgetset, callback) {
      log('Widgetset registered', widgetset);
      var ws = widgetsets[widgetset];
      if (ws && ws.pendingApps) {
        ws.callback = callback;
        for (var i = 0; i < ws.pendingApps.length; i++) {
          var appId = ws.pendingApps[i];
          log('Starting from register widgetset', appId);
          callback(appId);
        }
        ws.pendingApps = null;
      }
    };
  }

  log('Flow bootstrap loaded');
  if (appInitResponse.appConfig.productionMode && typeof window.__gwtStatsEvent != 'function') {
    window.Vaadin.Flow.gwtStatsEvents = [];
    window.__gwtStatsEvent = function (event) {
      window.Vaadin.Flow.gwtStatsEvents.push(event);
      return true;
    };
  }
  var config = appInitResponse.appConfig;
  var mode = appInitResponse.appConfig.productionMode;
  window.Vaadin.Flow.initApplication(config.appId, config);
};

export { init };
