import React, { useState, forwardRef, useRef, useImperativeHandle, ReactElement, memo } from 'react';
import message from 'components/message';
import { useSelector, useDispatch } from 'react-redux';
import JSZip from 'jszip';
import saveAs from 'file-saver';
import ExportAssets from 'components/ExportAssets';
import { exportScreen } from 'services/screen';
import { getConfigFromPanelConfig } from 'utils/screen';
import { getAssetsFromConfig } 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 { transformExportDataToLowerVersion4dot5 } from 'utils/patch';
import { ScreenItem } from 'models/screen.types';
import EasyvModal, { useConfirmRef, useActionModalRef } from 'components/EasyvModal';
import EasyvButton from 'components/EasyvButton';
import once from 'lodash/once';
import { useExportQueue } from '../../hooks/useExportQueue';
import EasyvScrollContainer from 'components/EasyvScrollContainer';

export interface ExportScreensModalRef {
  show: () => void;
}
export interface ExportScreensModalProps {
  list: Pick<ScreenItem, 'screenId' | 'screenName'>[];
  onComplete?: () => void;
  content?: ReactElement | string;
}

const EasyVExportScreensModal = forwardRef<ExportScreensModalRef, ExportScreensModalProps>((props, ref) => {
  const easyvConfirm = useConfirmRef();
  const actionRef = useActionModalRef();
  const dispatch = useDispatch();
  const { content } = props;
  const {
    userInfo: { tenantId }
  } = useSelector((state: any) => state.user);
  let { list: constants = [] } = useSelector((state: any) => state.constants);
  let { list: tenantConstants = [] } = useSelector((state: any) => state.tenantConstants);
  const [screens, setScreens] = useState<Pick<ScreenItem, 'screenId' | 'screenName'>[]>([]);
  const [completed, setCompleted] = useState<
    {
      id: number;
      zip: any;
      name: string;
    }[]
  >([]);
  const { current, watchBehavior, exportQueuelear } = useExportQueue({
    assets: screens,
    assetsKey: 'screenId'
  });

  useImperativeHandle(ref, () => ({
    show: () => {
      if (props.list.length) {
        easyvConfirm.current?.show();
        return;
      }
      message.error('请选择导出的大屏');
    }
  }));

  const { updateArrayWithObject } = Utils;
  const { extension } = exportConfig;

  const hideModal = () => {
    setScreens([]);
    setCompleted([]);
    exportQueuelear();
    actionRef.current?.close();
  };

  const onRequest = async (id) => {
    if (!constants.length) {
      constants = await dispatch(getConstants());
    }

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

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

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

        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/${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 compConfigList = getAssetsFromConfig(compConfig);
          return acc.concat(compConfigList).concat(getAssetsFromConfig(eventsConfig));
        }, []);

        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 onError = (id) => {
    watchBehavior({
      id,
      state: 1
    });
  };

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

  const onRemove = (id) => {
    const filterScreens = screens.filter((d) => d.screenId != id);
    setScreens(filterScreens);
    const filterCompleted = completed.filter((d) => d.id !== id);
    setCompleted(filterCompleted);
    // 当列表的所有的大屏被删除时，自动关闭modal
    if (filterScreens.length === 0) {
      setCompleted([]);
      actionRef.current?.close();
    }
    watchBehavior({
      id,
      state: -1
    });
  };

  const onDownloadAll = async () => {
    const hide = message.loading('下载中...', 0);
    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();
    });
    hide();
    actionRef.current?.close();
  };

  return (
    <>
      {/* 导出提示框 */}
      <EasyvModal.Confirm
        title="导出"
        iconType="info"
        onOkBtnType="primary"
        content={content}
        ref={easyvConfirm}
        onOk={() => {
          setScreens(props.list);
          easyvConfirm.current?.close();
          actionRef.current?.show();
        }}
      />
      {/* 批量导出下载操作框 */}
      <EasyvModal.ActionModal
        footer={
          <EasyvButton disabled={screens.length !== completed.length} theme="primary" onClick={once(onDownloadAll)}>
            下载
          </EasyvButton>
        }
        content={
          <EasyvScrollContainer>
            {screens.map((item) => (
              <ExportAssets
                start={current.includes(item.screenId)}
                key={item.screenId}
                name={`${item.screenName}_${item.screenId}`}
                id={item.screenId}
                onRequest={onRequest}
                onComplete={onComplete}
                onError={onError}
                onRemove={onRemove}
                trailColor="#3D404C"
              />
            ))}
          </EasyvScrollContainer>
        }
        onCancel={hideModal}
        title="批量导出应用"
        ref={actionRef}
      />
    </>
  );
});

export default memo(EasyVExportScreensModal);

export const useEasyVExportScreensModal = () => {
  return useRef<ExportScreensModalRef>(null);
};
