import React, { useState, useEffect, useCallback } from 'react';
import { connect } from 'react-redux';
import JSZip from 'jszip';
import saveAs from 'file-saver';
import ExportModal from '../themePanel/ExportModal';
import { exportModules } from 'models/module';
import { getAssetsFromConfig } from 'utils/component';
import exportConfig from 'constants/export-config';
import {getAssetsUrl} from 'utils/utils';
const { extension } = exportConfig;

function ExportContainer({
  children,
  list,
  exportModules,
  onComplete
}: {
  children: React.ReactElement;
  list: any;
  exportModules: (param) => Promise<{ modulesConfig: any; categoryConfig: any }>;
  onComplete?: () => void;
}) {
  const [visible, setVisible] = useState(false);
  const [total, setTotal] = useState(0);
  const [loaded, setLoaded] = useState(0);
  const [status, setStatus] = useState('normal');
  const [retryCount, setRetryCount] = useState(0);

  useEffect(() => {
    const controller = window.AbortController && new AbortController();
    const zipFiles = (zip, src) => {
      const signal = controller && controller.signal;
      const path = src.slice(0, src.lastIndexOf('/'));
      const name = src.slice(src.lastIndexOf('/') + 1);

      return fetch(getAssetsUrl(`${src}`), { signal, cache: 'no-store' })
        .then((res) => {
          if (res.status === 200) {
            return res.blob();
          } else {
            return Promise.reject(res.statusText);
          }
        })
        .then((result) => {
          setLoaded((prev) => prev + 1);
          zip.folder(path).file(name, result);
        });
    };

    async function startExport() {
      const result = await exportModules(list);

      if (result) {
        const { modulesConfig, categoryConfig } = result;

        const components: any[] = [];
        const screens: any[] = [];
        const getComponentsConfigAndScreenConfig = (config) => {
          config.forEach((d) => {
            if (d.statesConfig.length) {
              d.statesConfig.forEach((o: any) => {
                components.push(...o.componentsConfig);
                screens.push(o.screenConfig);
                if (o.panelsConfig.length) {
                  getComponentsConfigAndScreenConfig(o.panelsConfig);
                }
              });
            }
          });
        };

        modulesConfig.forEach((d) => {
          components.push(...d.componentsConfig);
          getComponentsConfigAndScreenConfig(d.panelsConfig);
        });

        const zip = new JSZip();

        // 数据
        zip.file('modulesConfig.json', JSON.stringify(modulesConfig));
        zip.file('categoryConfig.json', JSON.stringify(categoryConfig));

        // 文件
        let componentsFiles = components.reduce((all: any, item: any) => {
          if (!item.parent) {
            const base = JSON.parse(item.base);
            all.push(`components/${base.module_name}/${base.version}/${base.module_name}.js`);
          }
          return all;
        }, []);
        componentsFiles = Array.from(new Set(componentsFiles));

        let componentsThumbs = components.reduce((all: any, item: any) => {
          if (!item.parent) {
            const base = JSON.parse(item.base);
            if (base.thumb) {
              all.push(base.thumb);
            }
          }
          return all;
        }, []);
        componentsThumbs = Array.from(new Set(componentsThumbs));

        const componentsAssets = components.reduce((all, item) => all.concat(getAssetsFromConfig(JSON.parse(item.config))), []);
        const screensAssets = screens.reduce((all, item) => all.concat(getAssetsFromConfig(JSON.parse(item.config))), []);
        const modulesThumbs = modulesConfig.map((d) => d.moduleConfig.thumb);

        const assets = componentsThumbs.concat(componentsAssets).concat(screensAssets).concat(modulesThumbs);

        const all = componentsFiles
          .map((d) => zipFiles(zip, d))
          .concat(assets.map((d) => zipFiles(zip, d).catch((error) => console.error(error))));

        setTotal(all.length);

        try {
          await Promise.all(all);
          setStatus('success');
        } catch (error) {
          controller && controller.abort();
          setStatus('exception');
        }

        zip.generateAsync({ type: 'blob' }).then((blob) => {
          hideModal();
          onComplete && onComplete();
          if (list.length === 1) {
            saveAs(blob, `素材包_${modulesConfig[0].moduleConfig.name}${extension.module}`);
          } else {
            saveAs(blob, `素材包(${list.length})${extension.module}`);
          }
        });
      } else {
        controller && controller.abort();
        setStatus('exception');
      }
    }

    if (visible) {
      setTotal(0);
      setLoaded(0);
      setStatus('active');

      startExport();
    }
    return () => {
      controller && controller.abort();
    };
  }, [visible, retryCount, list]);

  const hideModal = useCallback(() => {
    setVisible(false);
  }, []);

  const handleRestart = useCallback(() => {
    setRetryCount((prev) => prev + 1);
  }, []);

  const percent = loaded ? Math.round((loaded / total) * 100) : 0;

  return (
    <span>
      <span onClick={() => setVisible(true)}>{children}</span>
      <ExportModal
        visible={visible}
        title="素材导出"
        status={status}
        statusText={status === 'success' ? '导出成功' : status === 'exception' ? '导出失败，请重试' : '素材正在导出，请稍后...'}
        percent={percent}
        onCancel={hideModal}
        onRestart={handleRestart}
      />
    </span>
  );
}

const mapDispatchToProps = {
  exportModules
};

export default connect(null, mapDispatchToProps)(ExportContainer);
