import virtualElement from 'virtual-element';
import isVirtual from './utils/isVirtual';
import isResource from './utils/isResource';
import callVirtual from './utils/callVirtual';
import evaluate from './utils/evaluate';
import PropTypes from 'proptypes';
import get from 'lodash.get';
import cloneDeepWith from 'lodash.clonedeepwith';
import isPlainObject from 'lodash.isplainobject';
import merge from 'lodash.merge';
import CookieManager from 'webmiddle-manager-cookie';

export { PropTypes };

export default class WebMiddle {
  static h(...args) {
    return virtualElement(...args);
  }

  isVirtual(...args) {
    return isVirtual.apply(this, args);
  }

  isResource(...args) {
    return isResource.apply(this, args);
  }

  callVirtual(...args) {
    return callVirtual.apply(this, args);
  }

  evaluate(...args) {
    return evaluate.apply(this, args);
  }

  constructor(options = {}) {
    this.name = options.name;

    this.parent = options.parent;

    this.services = {}; // <path, Service>

    const userServices = options.services || {};
    for (const path of Object.keys(userServices)) {
      this.registerService(path, userServices[path]);
    }

    this.settings = options.settings || {};

    global.webmiddle = global.webmiddle || {
      cookieManager: new CookieManager(),
    };
    this.cookieManager = global.webmiddle.cookieManager;
  }

  registerService(path, Service) {
    const ComposedService = ({ children, ...props }) => (
      <Service
        {...props}
      >
        {children}
      </Service>
    );
    ComposedService.propTypes = Service.propTypes;
    ComposedService.webmiddle = this;

    this.services[path] = ComposedService;
  }

  service(path) {
    const Service = this.services[path];
    if (Service) return Service;
    if (this.parent) return this.parent.service(path);
    return undefined;
  }

  setting(path) {
    const setting = get(this.settings, path);
    const parentSetting = this.parent ? this.parent.setting(path) : undefined;

    let finalSetting = setting;
    if (isPlainObject(setting) && isPlainObject(parentSetting)) {
      finalSetting = merge({}, parentSetting, setting);
    } else if (typeof setting === 'undefined' && typeof parentSetting !== 'undefined') {
      finalSetting = parentSetting;
    }

    return cloneDeepWith(finalSetting, value => {
      // don't try to clone functions (since they are converted to an empty object)
      if (typeof value === 'function') return value;
    });
  }

  log(...args) {
    if (this.setting('verbose')) console.log(...args);
  }
}

@@ -1412,24 +1412,22 @@
   const 
-Composed
+Higher
 Service 
@@ -1430,16 +1430,36 @@
 ice = (%7B
+ webmiddle, options,
  childre
@@ -1464,21 +1464,20 @@
 ren, ...
-props
+rest
  %7D) =%3E (
@@ -1508,13 +1508,12 @@
 %7B...
-props
+rest
 %7D%0A  
@@ -1561,32 +1561,30 @@
 %0A    );%0A    
-Composed
+Higher
 Service.prop
@@ -1614,24 +1614,22 @@
 es;%0A    
-Composed
+Higher
 Service.
@@ -1677,16 +1677,14 @@
 %5D = 
-Composed
+Higher
 Serv

fix registerService