import React, { ReactNode, useState, useEffect } from 'react';
import message from 'components/message';
import { connect } from 'react-redux';
import JSZip from 'jszip';
import saveAs from 'file-saver';
import ConfirmModal from 'components/ConfirmModal';
import ExportAssets from 'components/ExportAssets';
import { exportScreen } from 'services/screen';
import { getConfigFromPanelConfig } from 'utils/screen';
import { getAssetsFromConfig, getAssetsEventComponentConfig } from 'utils/component';
import exportConfig from 'constants/export-config';
import { Utils } from '@easyv/react-gui';
import { getConstants } from 'models/constant';
import { getConstants as getTenantConstants } from 'models/tenant-constant';
import { ScreenItem } from 'models/screen.types';
import { transformExportDataToLowerVersion4dot5 } from 'utils/patch';
import { fixMissingComponents, fixPanelMissingComponents } from 'utils/fix-errors';

interface IProps {
  tenantId: number;
  list: Pick<ScreenItem, 'screenId' | 'screenName'>[];
  constants: any[];
  tenantConstants: any[];
  getConstants: () => any;
  getTenantConstants: () => any;
  onComplete?: () => void;
  children: ReactNode;
  theme?: 'white' | 'black';
}

function ExportScreenModal(props: IProps) {
  const [version, setVersion] = useState<'new' | 'old' | null>(null);
  const [visible, setVisible] = useState<boolean>(false);
  const [screens, setScreens] = useState<Pick<ScreenItem, 'screenId' | 'screenName'>[]>([]);
  const [completed, setCompleted] = useState<
    {
      id: number;
      zip: any;
      name: string;
    }[]
  >([]);

  // const { confirm } = modal;
  const { updateArrayWithObject } = Utils;
  const { extension } = exportConfig;

  useEffect(() => {
    if (version) {
      showModal();
    }
  }, [version]);

  const showModal = () => {
    const { list } = props;
    setVisible(true);
    setScreens(list);
  };

  const hideModal = () => {
    setVisible(false);
    setScreens([]);
    setCompleted([]);
    setVersion(null);
  };

  const onRequest = async (id) => {
    let { constants = [], tenantConstants = [] } = props;
    const { getConstants, getTenantConstants } = props;
    if (!constants.length) {
      constants = await getConstants();
    }

    if (!tenantConstants.length) {
      tenantConstants = await getTenantConstants();
    }

    return exportScreen(id).then(async (res: any) => {
      if (res) {
        const data = transformExportDataToLowerVersion4dot5(res); // 需要导出的数据

        let { componentsConfig, panelConfig } = data;
        const { screenConfig, sourceConfig } = data;

        // 修复组件丢失或者子组件失去绑定关系导致导出失败的问题
        componentsConfig = fixMissingComponents(componentsConfig, JSON.parse(screenConfig.layers || '[]'));
        panelConfig = fixPanelMissingComponents(panelConfig);

        const componentConfig = componentsConfig.concat(getConfigFromPanelConfig(panelConfig, 'componentsConfig'));
        const allScreenConfig = [screenConfig].concat(getConfigFromPanelConfig(panelConfig, 'screenConfig'));
        const allSources = sourceConfig.concat(getConfigFromPanelConfig(panelConfig, 'sourceConfig'));

        const components = componentConfig
          .filter((d) => !d.parent)
          .map((d) => {
            const base = JSON.parse(d.base);

            return `${d.from === 1 ? `tenants/${props.tenantId}/` : ''}components/${base.module_name}/${base.version}/${
              base.module_name
            }.js`;
          });
        const thumbs = componentConfig
          .filter((d) => !d.parent)
          .map((d) => {
            const base = JSON.parse(d.base);
            const constant =
              d.from === 1
                ? tenantConstants && tenantConstants.find((o) => o.moduleName === base.module_name)
                : constants && constants.find((o) => o.moduleName === base.module_name);
            return constant && constant.thumb;
          })
          .filter((d) => d);
        const configAssets = componentConfig.reduce((acc, d) => {
          const compConfig = JSON.parse(d.config);
          const compEvents = d.events ? JSON.parse(d.events) : [];
          const eventsConfig = compEvents.reduce(
            (all, o) =>
              o.componentConfig && Object.keys(o.componentConfig).length > 0 ? updateArrayWithObject(compConfig, o.componentConfig) : all,
            []
          );

          const eventComponentConfig = compEvents.reduce(
            (all, o) =>
              o.componentConfig && Object.keys(o.componentConfig).length > 0
                ? all.concat(getAssetsEventComponentConfig(o.componentConfig))
                : all,
            []
          );
          const compConfigList = getAssetsFromConfig(compConfig);

          return acc.concat(compConfigList).concat(getAssetsFromConfig(eventsConfig)).concat(eventComponentConfig);
        }, []);

        const backgroundImgs = allScreenConfig.reduce((acc, d) => acc.concat(getAssetsFromConfig(JSON.parse(d.config))), []);
        const csvFiles = allSources.reduce((all, item) => {
          if (item.type == 1) {
            // csv类型数据源
            const config = JSON.parse(item.config);
            return all.concat(config.filepath);
          } else {
            return all;
          }
        }, []);

        const screenThumb = screenConfig.thumb;
        const files = Array.from(new Set(components)).filter((d) => d);
        const assets = Array.from(new Set(thumbs.concat(screenThumb).concat(configAssets).concat(backgroundImgs).concat(csvFiles))).filter(
          (d) => d
        );

        return { data, files, assets };
      } else {
        return Promise.reject();
      }
    });
  };

  const onComplete = ({ id, zip, name }) => {
    if (screens.length === 1) {
      hideModal();
      props.onComplete && props.onComplete();
      zip.generateAsync({ type: 'blob' }).then((blob) => {
        saveAs(blob, `${name}${extension.screen}`);
      });
    } else {
      setCompleted((next) => next.concat([{ id, zip, name }]));
    }
  };

  const onRemove = (id) => {
    setScreens(screens.filter((d) => d.screenId != id));
  };

  const onDownloadAll = async () => {
    const allZip = new JSZip();
    await Promise.all(
      completed.map((d) => {
        return new Promise((resolve) => {
          d.zip.generateAsync({ type: 'blob' }).then((blob) => {
            // 文件导出时，文件名中的 / 会与系统路径存在冲突，被识别为文件夹+文件名，单个文件系统会自行处理，多个文件打包成一个压缩包时，不会出来，需要自己手动替换文件名中的 / 为 _
            allZip.file(`${d.name.replace(/\//g, '_')}${extension.screen}`, blob);
            resolve(blob);
          });
        });
      })
    );
    allZip.generateAsync({ type: 'blob' }).then((blob) => {
      saveAs(blob, `screens(${completed.length})${extension.screen}`);
      hideModal();
      props.onComplete && props.onComplete();
    });
  };

  const onClick = () => {
    // eslint-disable-next-line react-hooks/rules-of-hooks
    const { list } = props;
    if (list.length) {
      showModal();
    } else {
      message.error('请选择需要导出的大屏');
    }
  };

  const { children, theme = 'white' } = props;
  const ConfirmModalProps: {
    okText: string;
    footer: boolean | null;
  } = {
    okText: '下载',
    footer: true
  };
  if (screens.length === 1) {
    ConfirmModalProps.footer = null;
  }
  return (
    <span>
      <span onClick={onClick}>{children}</span>
      {visible && (
        <ConfirmModal
          title="导出大屏"
          theme={theme}
          visible={visible}
          onClose={hideModal}
          onOk={onDownloadAll}
          {...ConfirmModalProps}
          cancelText={''}>
          <div className="export-container">
            {screens.map((item) => (
              <ExportAssets
                key={item.screenId}
                trailColor={theme === 'black' ? '#383B4A' : ''}
                name={`${item.screenName}_${item.screenId}`}
                id={item.screenId}
                onRequest={onRequest}
                onComplete={onComplete}
                onRemove={onRemove}
              />
            ))}
          </div>
        </ConfirmModal>
      )}
    </span>
  );
}

const mapStateToProps = ({ user, constants, tenantConstants }) => {
  const { userInfo } = user;
  return {
    tenantId: userInfo.tenantId,
    constants: constants.list,
    tenantConstants: tenantConstants.list
  };
};

const mapDispatchToProps = {
  getConstants,
  getTenantConstants
};

export default connect(mapStateToProps, mapDispatchToProps)(ExportScreenModal);
