import React from 'react';
import ReactDOM from 'react-dom';
import uuid from 'uuid';
import Events from 'ampersand-events';

function formatEventDetails({name, data}) {
  const [resource, eventName] = name.split(':');
  const details = {
    resource,
    event: eventName,
    data
  };

  if (data.actorId) {
    details.actorId = data.actorId;
  }

  if (data.action) {
    details.action = data.action;
  }

  return details;
}

function BrowserWidget(el) {
  this.el = el;
  this.remove = (callback) => new Promise((resolve) => {
    const id = this.el.getAttribute('data-uuid');
    // Unmount React Component
    const removed = ReactDOM.unmountComponentAtNode(el);

    // Remove from widgetStore
    Reflect.deleteProperty(window.ciscospark.widgetStore, id);

    // Fire callback
    if (typeof callback === 'function') {
      return callback(removed);
    }

    return resolve(removed);
  });

  function handleEvent(name, data) {
    if (this.el) {
      // Don't nest data if the event data object is already constructed
      const detail = data.data ? data : formatEventDetails({name, data});
      // Dispatch DOM event
      const event = new CustomEvent(name, {
        detail
      });
      this.el.dispatchEvent(event);

      // Trigger ampersand events
      this.trigger(name, detail);
    }

    return this;
  }

  this.handleEvent = handleEvent;
  this.version = process.env.REACT_CISCOSPARK_VERSION;

  // Attach event handlers to each widget
  const widgetFn = {};
  Object.keys(window.ciscospark.widgetFn).forEach((fn) => {
    const func = window.ciscospark.widgetFn[fn];
    widgetFn[fn] = (options) => {
      const onEvent = (name, data) => {
        this.handleEvent(name, data);
        if (typeof options.onEvent === 'function') {
          options.onEvent(name, data);
        }
      };
      return func.call(this, {
        ...options,
        onEvent: onEvent.bind(this)
      });
    };
  });

  Object.assign(this, widgetFn);

  return this;
}


/**
 * Takes a DOM element and returns a widget or initializes it as a widget element
 * @param {Element} el
 * @returns {Object}
 */
function getWidget(el) {
  /**
   * Creates a new widget object and stores it
   * @param  {object} widgetEl HTML element where a widget is mounted
   * @returns {object} widgetObject
   */
  function createNewWidget(widgetEl) {
    Object.assign(BrowserWidget.prototype, Events);
    const widgetObj = new BrowserWidget(widgetEl);

    const id = uuid.v4();
    // Store ID as attribute on dom element
    widgetEl.setAttribute('data-uuid', id);
    window.ciscospark.widgetStore[id] = widgetObj;
    return widgetObj;
  }

  /**
   * Retrieves a Widget from global widget store
   * @param  {object} widgetEl HTML element where a widget is mounted
   * @returns {object} widgetObject
   */
  function selectWidget(widgetEl) {
    // Check if element exists and is HTML Element
    if (widgetEl && widgetEl.nodeType === 1) {
      // Get ID as attribute on dom element
      const id = widgetEl.getAttribute('data-uuid');
      // Add class
      widgetEl.classList.add('ciscospark-widget');
      // Get widget from store if it exists
      const widgetObj = window.ciscospark.widgetStore[id];
      if (widgetObj) {
        return widgetObj;
      }
      // Otherwise create new widget store
      return createNewWidget(widgetEl);
    }
    console.warn('WARNING: ciscospark.widget: No HTML element provided.'); // eslint-disable-line no-console
    return false;
  }

  return selectWidget(el);
}


function setupSparkWidgetNamespace() {
  if (!window.ciscospark) {
    window.ciscospark = {};
  }
  if (!window.ciscospark.widgetStore) {
    window.ciscospark.widgetStore = {};
  }
  if (!window.ciscospark.widgetFn) {
    window.ciscospark.widgetFn = {};
  }
  if (!window.ciscospark.widget || typeof window.ciscospark.widget !== 'function') {
    window.ciscospark.widget = getWidget;
  }
}

/**
 * HOC that registers widget to browser globals
 * @param {String} name of widget to use
 * @returns {Function}
 */
export default function withBrowserGlobals({name}) {
  // Inject widget into browser globals
  setupSparkWidgetNamespace();
  const fnName = `${name}Widget`;

  return (BaseComponent) => {
    function renderSparkWidget(options) {
      const {el} = this;
      const {onEvent, ...otherOptions} = options;

      // Instantiate Component into DOM
      ReactDOM.render(<BaseComponent onEvent={onEvent} {...otherOptions} />, el);

      return this;
    }

    // Set up this widget's init process
    // Allow dev to call window.ciscospark.widget(EL).WIDGETNAME({options});
    if (typeof window.ciscospark.widgetFn[fnName] === 'function') {
      // eslint-disable-reason Need to log console warning when widget already registered
      // eslint-disable-next-line no-console
      console.warn(`${name} Widget is already registered.`);
    }
    else {
      window.ciscospark.widgetFn[fnName] = renderSparkWidget;
      // Attach version number statically. This will need to change when we publish widgets independently
      window.ciscospark.widgetFn[fnName].version = process.env.REACT_CISCOSPARK_VERSION;
    }

    return BaseComponent;
  };
}

@@ -1787,37 +1787,65 @@
 ns.onEvent(name,
+ formatEventDetails(%7Bname,
  data
+%7D)
 );%0A        %7D%0A   

Fix event details structure