import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { Portal } from 'amos-framework';

const IS_REACT_16 = !!ReactDOM.createPortal;

/**
 * portal component
 *
 * @param {any} WrappedComponent
 * @param {any} options
 * @returns enhance Component
 */
export default function(WrappedComponent, options) {
  class WrapperComponent extends Component {
    static propTypes = {
      visible: PropTypes.bool,
      getContainer: PropTypes.func
    };

    constructor(props) {
      super(props);
      this.state = {
        visible: false
      };
    }

    shouldComponentUpdate({ visible }) {
      return !!(this.props.visible || visible);
    }

    componentWillUnmount() {
      if (IS_REACT_16) {
        return;
      }
      if (this.props.visible) {
        this.renderComponent({
          afterLeave: this.removeContainer,
          onLeave() {},
          visible: false
        });
      } else {
        this.removeContainer();
      }
    }

    getComponent = (extra = {}) => {
      return <WrappedComponent ref={node => (this._component = node)} {...this.props} {...extra} key="portal-wrapper" />;
    };

    getContainer = () => {
      if (this.props.getContainer) {
        return this.props.getContainer();
      }
      const container = document.createElement('div');
      document.body.appendChild(container);
      return container;
    };

    removeContainer = () => {
      if (this._container) {
        const container = this._container;
        ReactDOM.unmountComponentAtNode(container);
        container.parentNode.removeChild(container);
        this._container = null;
      }
    };

    renderComponent = (componentArg, ready) => {
      const { visible } = this.props;

      if (!visible || this._component) {
        if (!this._container) {
          this._container = this.getContainer();
        }
        const component = this.getComponent(componentArg);

        ReactDOM.unstable_renderSubtreeIntoContainer(this, component, this._container, function callback() {
          this._component = this;
          if (ready) {
            ready.call(this);
          }
        });
      }
    };

    render() {
      const { visible } = this.props;

      let portal = null;

      if (!IS_REACT_16) {
        return portal;
      }

      if (visible || this._component) {
        portal = <Portal getContainer={this.getContainer}>{this.getComponent()}</Portal>;
      }

      return portal;
    }
  }

  return WrapperComponent;
}
