import React, { Component } from 'react';
import Loading from '../../components/Loading';
import babelPluginTransfromDecoratorLegacy from 'babel-plugin-transform-decorators-legacy';
import {
  babelReady,
  isAlibaba,
  loadScriptAsync,
  REACT_CDN,
  PROP_TYPES_CDN,
  BASE_CDN,
  BASE_CSS_CDN,
  SASS_CDN,
} from '../../utils';
import './PlaygroundPreview.scss';

/**
 * babel ready -> compile -> load iframe
 */
import '../../service/common-externals';

export default class PlaygroundPreview extends Component {
  static displayName = 'PlaygroundPreview';

  static propTypes = {};

  static defaultProps = {};

  constructor(props) {
    super(props);
    this.id = parseInt(Math.random() * 99999);
    this.state = {
      compiledCSS: '',
      compiledJavaScript: '',
      ready: false,
    };
    this.externals = [];
  }

  async componentDidMount() {
    const { dataSource, mode } = this.props;
    if (isAlibaba) {
      const _externals = await this.getExternalResources();
      Object.keys(_externals).forEach((modName) => {
        this.externals.push(`'${modName}': '${_externals[modName]}',`);
      });
    }
    if (dataSource.jsx === '') {
      return;
    }

    if (mode === 'iframe') {
      await this.initIframe(dataSource);
    } else {
      await this.initContext(dataSource);
    }
  }

  getExternalResources = () => {
    return new Promise((resolve) => {
      require.ensure(
        [],
        (require) => {
          const externals = require('../../service/externals');
          resolve(externals);
        },
        'externals'
      );
    });
  };

  initIframe = async (dataSource) => {
    this.babel = await babelReady();
    const { code: compiledJavaScript } = await this.compileJavaScript(
      dataSource.jsx
    );
    const compiledCSS = await this.compileSCSS(dataSource.scss);

    this.setState({
      ready: true,
      compiledCSS,
      compiledJavaScript,
    });
  };

  initContext = async (dataSource) => {
    await Promise.all([
      loadScriptAsync(BASE_CDN),
      loadScriptAsync(PROP_TYPES_CDN),
    ]);
    if (!React.PropTypes) {
      React.PropTypes = window.PropTypes;
    }
    const { code: compiledJavaScript } = await this.compileJavaScript(
      dataSource.jsx
    );
    const compiledCSS = await this.compileSCSS(dataSource.scss);
    const { mountNode } = this;
    this.setState({ ready: true, compiledCSS, compiledJavaScript }, () => {
      if ('clear' in console) {
        console.clear();
      }
      eval(`
;(function() {
  var UMD = {
    'react': 'React',
    'react-dom': 'ReactDOM',
    'prop-types': 'PropTypes',
    'classnames': 'ClassNames',
    '@icedesign/base': 'ICEDesignBase',
    '@alife/next': 'ICEDesignBase',
    '@ali/ice': 'ICEDesignBase',
    'react-copy-to-clipboard': 'ReactCopyToClipBoard',
    '@icedesign/img': 'IceImg',
    '@icedesign/container': 'IceContainer',
    '@icedesign/label': 'IceLabel',
    '@icedesign/panel': 'IcePanel',
    '@icedesign/icon': 'IceIcon',
    '@icedesign/ellipsis': 'IceEllipsis',
    '@icedesign/layout': 'IceLayout',
    '@icedesign/demo-layout': 'IceDemoLayout',
    '@icedesign/form-binder': 'IceFormBinder',
    '@icedesign/menu': 'IceMenu',
    '@icedesign/data-binder': 'IceDataBinder',
    '@icedesign/title': 'IceTitle',
    '@icedesign/styled-menu': 'IceStyledMenu',
    '@icedesign/awesome-icon': 'IceAwesomeIcon',
    'foundation-symbol': 'FoundationSymbol',
    'dynamic-icon': 'DynamicIcon',
    ${this.externals.join('\n')}
  };

  function require(mod) {
    if (UMD[mod]) {
      return window[UMD[mod]];
    } else {
      console.warn('require', mod, 'not found');
    }
  }

  ${compiledJavaScript}

})();`);
    });
  };

  async componentWillReceiveProps(nextProps) {
    const { mode } = nextProps;
    if (
      nextProps.dataSource.jsx !== this.props.dataSource.jsx ||
      nextProps.dataSource.scss !== this.props.dataSource.scss ||
      nextProps.mode !== this.props.mode
    ) {
      this.setState({ ready: false });

      if (mode === 'iframe') {
        await this.initIframe(nextProps.dataSource);
      } else {
        await this.initContext(nextProps.dataSource);
      }
    }
  }

  compileJavaScript = (es6) => {
    return babelReady().then((babel) => {
      return new Promise((resolve, reject) => {
        try {
          const compiled = babel.transform(es6, {
            presets: ['es2015', 'react', 'stage-0'],
            plugins: [babelPluginTransfromDecoratorLegacy],
          });
          resolve(compiled);
        } catch (err) {
          reject(err);
        }
      });
    });
  };

  compileSCSS = (scss) => {
    return loadScriptAsync(SASS_CDN).then(() => {
      return new Promise((resolve, reject) => {
        window.sass.compile(
          `.playground-preview-id${this.id} {${scss}}`,
          (result) => {
            resolve(result.text);
          }
        );
      });
    });
  };

  generateScript = (src) => `<script src="${src}"></script>`;
  generateCSS = (src) =>
    `<link rel="stylesheet" type="text/css" href="${src}">`;
  generateSrcDoc = () => {
    const { compiledCSS, compiledJavaScript } = this.state;
    // srcdoc 和外部必须使用同一份 React/ReactDOM 实例
    const reactCDNScript = `
      <script>
        window.React = top.React;
        window.ReactDOM = top.ReactDOM;
      </script>
    `;
    // this.generateScript(REACT_CDN);
    const baseCDNScript = this.generateScript(BASE_CDN);
    const propTypesCDNScript = this.generateScript(PROP_TYPES_CDN);
    const baseCDNCSS = this.generateCSS(BASE_CSS_CDN);
    const pageInitial = `var mountNode = document.querySelector('#ice-container');
React.PropTypes = require('prop-types'); // polyfill for React Over 15.6.0
`;

    const requireDefinition = `
      var UMD = {
        'react': 'React',
        'react-dom': 'ReactDOM',
        'prop-types': 'PropTypes',
        '@icedesign/base': 'ICEDesignBase',
        '@alife/next': 'ICEDesignBase',
        '@ali/ice': 'ICEDesignBase',
        'react-copy-to-clipboard': 'ReactCopyToClipBoard',
        '@icedesign/img': 'IceImg',
        '@icedesign/container': 'IceContainer',
        '@icedesign/label': 'IceLabel',
        '@icedesign/panel': 'IcePanel',
        '@icedesign/icon': 'IceIcon',
        '@icedesign/ellipsis': 'IceEllipsis',
        '@icedesign/layout': 'IceLayout',
        '@icedesign/demo-layout': 'IceDemoLayout',
        '@icedesign/form-binder': 'IceFormBinder',
        '@icedesign/menu': 'IceMenu',
        '@icedesign/data-binder': 'IceDataBinder',
        '@icedesign/title': 'IceTitle',
        '@icedesign/styled-menu': 'IceStyledMenu',
        '@icedesign/awesome-icon': 'IceAwesomeIcon',
        'foundation-symbol': 'FoundationSymbol',
        'dynamic-icon': 'DynamicIcon',
        ${this.externals.join('\n')}
      };
      window.require = function browserRequire(mod) {
        if (UMD[mod]) {
          return window[UMD[mod]] || top[UMD[mod]];
        } else {
          console.warn('require', mod, 'not found');
        }
      };
    `;

    return `<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  ${baseCDNCSS}
  <title>ICE Playground Preview</title>
  ${reactCDNScript}
  ${baseCDNScript}
  ${propTypesCDNScript}
  <script>${requireDefinition}</script>
  <style>${compiledCSS}</style>
</head>

<body class="playground-preview-id${this.id}">
  <div id="ice-container"></div>
  <script>;(function(require, exports, module) {
    ${pageInitial}${compiledJavaScript}
  })(window.require, {}, { require: window.require, exports: {}});</script>
</body>
</html>
`;
  };

  renderIframe = () => {
    const { style } = this.props;
    const { ready } = this.state;

    if (ready) {
      if ('clear' in console) {
        console.clear();
      }
      return (
        <div className="playground-preview" style={style}>
          <iframe
            className="playground-iframe"
            ref={(ref) => {
              this.iframe = ref;
            }}
            scrolling="yes"
            sandbox="allow-scripts allow-same-origin allow-modals allow-popups allow-forms"
            srcDoc={this.generateSrcDoc()}
          />
        </div>
      );
    } else {
      return <Loading size="small" text="隔离预览层加载中" />;
    }
  };

  renderContext = () => {
    const { style } = this.props;
    const { compiledCSS, compiledJavaScript } = this.state;
    return (
      <div
        className={`playground-preview playground-preview-id${this.id}`}
        style={style}
      >
        <style
          dangerouslySetInnerHTML={{
            __html: compiledCSS,
          }}
        />
        <div
          ref={(ref) => {
            this.mountNode = ref;
          }}
        />
      </div>
    );
  };

  render() {
    const { mode = 'iframe' } = this.props;
    if (mode === 'iframe') {
      return this.renderIframe();
    } else {
      return this.renderContext();
    }
  }
}
