import isPlainObject from 'lodash/isPlainObject';
import isNumber from 'lodash/isNumber';
import { api } from 'constants/config';
import { actionTypes } from 'constants/action-types';
import { getScreen } from 'models/screen';
import { addRecycleBin } from 'models/commentDelted';
import { updatePanel, getPanelInfoForScreenEdit } from 'models/panel';
import { getTopScreenId } from 'utils/screen';
import { addUndoConfig, selectLayerWithScrollIntoView } from 'models/create-app';
import http, { request } from 'utils/fetch';
import { deepUpdate, updateByKeyname, deleteByKey } from 'utils/data-helper';
import qs from 'qs';
import {
  getDimensionFromChildren,
  componentOperator,
  transformComponentToServer,
  getComponent,
  getCenterFromDimension,
  mergeFields,
  getComponentDataApiByPathName,
  getReplacedCallbackApiQuery,
  getReplacedCallbackSqlQuery,
  getCurComponentCallbackValues,
} from 'utils/component';
import { toObject, getGroupChildren, getFirstSelectedId, insertLayerById } from 'utils/layer';
import { getScreenId, screenOperator, getScreenCenter, isMobileScreen } from 'utils/screen';
import { eventTracking } from '@/models/event-tracking';
import {
  jsonToFormdata,
  isComponent,
  isGroup,
  isGeneralPanel,
  updateDataByKey,
  getAssetsUrl,
} from 'utils/utils';

import { uploadAssets, getImageRect, getVideoRect } from 'utils/upload';
import { getId } from 'utils/interaction';
import { Utils } from '@easyv/react-gui';
import { ComponentItem, ComponentsById } from './component.types';
import { Base64 } from 'js-base64';
import { Layers } from './screen.types';
import { Panel } from './panel.types';
import { Filters } from './filter.types';
import { ThemeDetail } from './theme.types';
import { JsQueue } from 'utils/queue';
import { updateNonentityModule } from 'models/user';
import { getContainerData } from 'models/data-container';
import { clearThemeComponentBackUp } from 'models/theme';
import { resetScreenConfig } from 'models/screen';
import { DataFrom } from 'models/component.types';
import message from 'components/message';
import { isCancelRequestData } from 'utils/convert';
import { dispatchDebugScreenEvent, getDebugConfigFromSessionStorage } from '@/utils/debug-screen';
import { CollectDataTs } from '@/utils/eventTracking/component.type';
const jsQueue = new JsQueue();
jsQueue.start();

const { updateArrayConfig, getComponentDimension, getConfig, mergeConfig, mergeStyleConfig } =
  Utils;

const signalStore = {};
const componentDataList: any = {}; //组件数据请求记录

function getKey(updateParam: any) {
  const { id, query, params, body, path, headers, callbackValues } = updateParam;
  return JSON.stringify({
    id,
    query,
    body,
    path,
    params,
    headers,
    callbackValues,
  });
}

/**
 *
 * @param id
 * @param controller  用于中断请求 一个是来源于组件  一个自身请求创建
 * @returns 组件数据请求包装器，将请求携带的平台的回调替换成真实的键值，根据是否是前端发起配置派发不同的请求
 */
export function getComponentData(id, controller?) {
  if (isCancelRequestData()) return;
  return (dispatch, getState) => {
    const { componentsById, callback } = getState();
    const component = componentsById[id];
    if (component) {
      const { dataType, dataConfigs, baseURL } = component;
      if (dataConfigs && dataConfigs[dataType]) {
        const { config = {} } = dataConfigs[dataType];

        let updateParam = {
          id,
        } as {
          callbackValues?: Record<string, any>;
          id: number;
          params?: string;
          path?: string;
          headers?: string;
          body?: string;
        };

        if (['api', 'apiGateway', 'dtInsight'].includes(dataType)) {
          // WARNING 当用户没有配置headers、path、params、body时是否可以以空字符串当默认值传给服务端
          const { callbackValues } = callback;
          updateParam = {
            ...updateParam,
            ...getReplacedCallbackApiQuery({ callbackValues, config }),
          };
          const { params = '', path = '', headers = '', body = '' } = updateParam;
          // 如果替换完之后还存在:开头的说明被替换的存在undefined
          if (
            /:([a-zA-Z]\w*)|(\$\{([\w\W]+?)\}\$)/.test(params) ||
            /:([a-zA-Z]\w*)|(\$\{([\w\W]+?)\}\$)/.test(path) ||
            /:([a-zA-Z]\w*)|(\$\{([\w\W]+?)\}\$)/.test(headers) ||
            /:([a-zA-Z]\w*)|(\$\{([\w\W]+?)\}\$)/.test(body)
          ) {
            return;
          }
          if (!config.reqFromBack) {
            if (baseURL) {
              dispatch(
                getComponentDataFromClient({
                  url: baseURL + path,
                  method: config.method,
                  needCookie: config.needCookie,
                  controller,
                  ...updateParam,
                }),
              );
            }
            return;
          }
        }

        if (
          ['mysql', 'oracle', 'mssql', 'postgresql', 'clickhouse', 'db2', 'damengdb'].includes(
            dataType,
          )
        ) {
          updateParam = {
            ...updateParam,
            ...getReplacedCallbackSqlQuery({ config, id, callback }),
          };
          const { callbackValues = {} } = updateParam;
          if (Object.values(callbackValues).some((d) => d === undefined)) {
            return;
          }
        }

        const key = getKey(updateParam);
        if (componentDataList[key]) {
          // 上次请求未结束，本次不发起
          return;
        }
        signalStore[id] && signalStore[id].abort(); // 取消请求
        signalStore[id] =
          controller?.signal?.aborted === false
            ? controller
            : window.AbortController && new AbortController();
        const signal = signalStore[id] && signalStore[id].signal;

        dispatch(getComponentDataFromServer({ ...updateParam, signal }));
      }
    }
  };
}
/**
 *
 * @param updateParam
 * @returns 服务端发起获取组件数据请求
 */
function getComponentDataFromServer(updateParam) {
  const { dataRequest, debugListenStatus } = getDebugConfigFromSessionStorage();
  return async (dispatch, getState) => {
    const { id: componentId, params, body, path, headers, callbackValues, signal } = updateParam;
    const { componentsById, callback } = getState();
    const component = componentsById[componentId] || {};
    const _key = component && getKey(updateParam);
    const { dataType } = component;
    const url = getComponentDataApiByPathName();

    const _originQuery = params ? `&${params}` : '';
    componentDataList[_key] = componentId;

    let _headers = {};
    if (/\/component\/data\/get/.test(url)) {
      // todo 后端项目在重构，配合加个code ，他们在网关做转发
      _headers['data-type'] = dataType;
    }

    const data = await jsQueue.http({
      http: http.post,
      body: [
        `${url}?componentId=${componentId}${_originQuery}`,
        { componentId, query: params, path, body, callbackValues, headers }, // headers 是个参数，后端请求
        {
          signal: signal,
          headers: _headers, // 是component/data/get 的 header
          error(response) {
            dispatchDebugScreenEvent<'error', 'dataRequest'>({
              type: 'error',
              id: componentId,
              name: component?.name,
              dispatchFrom: 'dataRequest',
              debugMsg: {
                message: response.message,
                callbackValues:
                  getCurComponentCallbackValues(componentId, callback, 'component') || {},
                data: '暂未获取到数据',
              },
            });
            dispatch({
              type: actionTypes.COMPONENT_DATA_GET_FAILURE,
              payload: {
                id: componentId,
                data: response.data,
                message: response.message,
              },
            });
          },
        },
      ],
    });
    if (componentDataList[_key]) {
      componentDataList[_key] = null;
    }
    if (data) {
      dispatch({
        type: actionTypes.COMPONENT_DATA_GET_SUCCESS,
        payload: {
          id: componentId,
          data,
        },
      });
      dataRequest &&
        debugListenStatus === 'recording' &&
        dispatchDebugScreenEvent<'info', 'dataRequest'>({
          type: 'info',
          id: componentId,
          dispatchFrom: 'dataRequest',
          name: component?.name,
          debugMsg: {
            data,
            callbackValues: getCurComponentCallbackValues(componentId, callback, 'component') || {},
          },
        });
    }
  };
}

/**
 *
 * @param updateParams
 * @returns 客户端发起获取组件数据请求
 */
function getComponentDataFromClient(updateParams) {
  const { dataRequest, debugListenStatus } = getDebugConfigFromSessionStorage();
  return async (dispatch, getState) => {
    let { url, headers, body } = updateParams;
    const { method = 'get', id: componentId, params = '', needCookie, controller } = updateParams;
    const { componentsById, callback } = getState();
    const component = componentsById[componentId] || {};
    const _key = getKey(updateParams);
    if (!url) {
      return;
    }
    url = params
      ? url.indexOf('?') > -1
        ? `${url}&${params}`
        : `${url}?${params.replace('?', '')}`
      : url;

    try {
      headers = headers ? JSON.parse(headers) : undefined;
      if (['post', 'patch', 'put'].includes(method) && headers && headers['Content-Type'] && body) {
        switch (headers['Content-Type']) {
          case 'multipart/form-data':
            body = jsonToFormdata(JSON.parse(body));
            break;
          case 'application/x-www-form-urlencoded':
            body = qs.stringify(JSON.parse(body));
            break;
          default:
            break;
        }
      }

      if (componentDataList[_key]) {
        return;
      }

      signalStore[componentId] && signalStore[componentId].abort(); // 取消请求
      signalStore[componentId] =
        controller?.signal?.aborted === false
          ? controller
          : window.AbortController && new AbortController();
      const signal = signalStore[componentId] && signalStore[componentId].signal;
      componentDataList[_key] = componentId;
      const result = await fetch(url, {
        method: method.toUpperCase(),
        credentials: needCookie ? 'include' : 'omit',
        headers: headers,
        body: ['post', 'patch', 'put'].includes(method) ? body : undefined,
        signal,
      })
        .then((response) => {
          if (response.status >= 200 && response.status < 300) {
            return response.json();
          } else {
            const error: any = new Error(`${response.statusText}status code${response.status}`); //**拼接，日志信息以**split
            error.response = response;
            throw error;
          }
        })
        .catch((err) => {
          throw new Error(`${err.message}暂未捕获到status code，请自行前往开发者工具查看`);
        });
      // 成功的时候会执行
      if (componentDataList[_key]) {
        componentDataList[_key] = null;
      }
      // const { componentsById } = getState();
      // dispatch(
      //   refreshDynamicData({ componentId, dataType: componentsById[componentId].dataType, data: Array.isArray(data) ? data : [data] })
      // );
      const data = Array.isArray(result) ? result : [result];
      dispatch({
        type: actionTypes.COMPONENT_DATA_GET_SUCCESS,
        payload: {
          id: componentId,
          data: data,
        },
      });
      dataRequest &&
        debugListenStatus === 'recording' &&
        dispatchDebugScreenEvent<'info', 'dataRequest'>({
          type: 'info',
          id: componentId,
          dispatchFrom: 'dataRequest',
          name: component?.name,
          debugMsg: {
            data,
            callbackValues: getCurComponentCallbackValues(componentId, callback, 'component') || {},
          },
        });
      return data;
    } catch (error: any) {
      dispatchDebugScreenEvent<'error', 'dataRequest'>({
        type: 'error',
        id: componentId,
        name: component?.name,
        dispatchFrom: 'dataRequest',
        debugMsg: {
          message: error.message,
          callbackValues: getCurComponentCallbackValues(componentId, callback, 'component') || {},
          data: '暂未获取到数据',
        },
      });
      console.error(error);
      // 取消请求的时候会执行
      if (componentDataList[_key]) {
        componentDataList[_key] = null;
      }
      if (error && error.name === 'AbortError') {
        return;
      }
      dispatch({
        type: actionTypes.COMPONENT_DATA_GET_FAILURE,
        payload: {
          id: componentId,
          data: [],
          message: error.message,
        },
      });
    }
  };
}

// 复制组件
export function copyComponents({ offsetX, offsetY }) {
  return async (dispatch) => {
    const copyComponentsInfo = dispatch(getSelectedComponentsInfo());
    if (!copyComponentsInfo) {
      return;
    }

    let { componentPositions, panelPositions } = copyComponentsInfo;
    const { id, components, children, panels, selected, originLayers } = copyComponentsInfo;

    if (offsetX || offsetY) {
      if (components.length) {
        componentPositions = [];
        components.forEach((item) => {
          const { left, top } = getComponentDimension(item.config);
          componentPositions.push({ left: left + offsetX, top: top + offsetY });
        });
      }
      if (panels.length) {
        panelPositions = [];
        panels.forEach((item) => {
          const config = item.config;
          panelPositions.push({ left: config.left + offsetX, top: config.top + offsetY });
        });
      }
    }

    try {
      await dispatch(
        copyLayersTo(
          {
            layers: originLayers,
            selected,
            components,
            children,
            panels,
            screenId: id,
            componentPositions,
            panelPositions,
          },
          id,
        ),
      );
    } catch (error) {
      console.error(error);
      message.error('复制失败');
    }
  };
}

// 将组件信息添加到localStorage中 拷贝组件
export function copyComponentsToClipboard(from = 'screen') {
  return (dispatch) => {
    const copyComponentsInfo = dispatch(getSelectedComponentsInfo());
    if (!copyComponentsInfo) {
      return;
    }

    localStorage.setItem(
      'easyv_component',
      JSON.stringify({
        from: from,
        data: copyComponentsInfo,
      }),
    );
  };
}

// 将组件样式添加到sessionStorage中
export function copyStyleToClipboard() {
  return (_dispatch, getState) => {
    const { createApp, componentsById } = getState();
    const { layersSelected, componentsChildSelected } = createApp;
    if (!layersSelected.length) {
      message.error('请先选择组件，再进行样式复制');
      return;
    }

    if (layersSelected.length > 1) {
      message.error('只能复制一个组件样式');
      return;
    }

    if (!isComponent(layersSelected[0])) {
      message.error('只能复制组件样式');
      return;
    }

    const componentId = componentsChildSelected ? componentsChildSelected : layersSelected[0];
    const component = componentsById[componentId];
    if (!component) {
      message.error('组件丢失，请刷新后重试');
      return;
    }

    const config = getConfig(component.config, 'style');
    sessionStorage.setItem(
      'component_style',
      JSON.stringify({
        moduleName: component.base.module_name,
        config: config,
      }),
    );
  };
}

// 获取选中的组件信息
function getSelectedComponentsInfo() {
  return (_dispatch, getState) => {
    const { createApp, panelsById, componentsById, screensById } = getState();
    const { id, layersSelected } = createApp;

    if (!layersSelected.length) {
      return;
    }

    const screen = screensById[id];
    const { layers } = screen;
    const layersById = toObject(layers);

    const panelIds: any = [];
    const _getComponentsId = (layers) => {
      return layers.reduce((all, item) => {
        if (isComponent(item)) {
          return all.concat(item);
        } else if (isGeneralPanel(item)) {
          panelIds.push(getId(item));
          return all;
        } else if (isGroup(item)) {
          return all.concat(_getComponentsId(getGroupChildren(layersById[item]).map((d) => d.id)));
        }
      }, []);
    };
    const componentIds = _getComponentsId(layersSelected);

    const getSimpleComponent = (component) => {
      const dataConfigs: any = {};
      for (const key in component.dataConfigs) {
        if (key === 'static') {
          dataConfigs.static = component.dataConfigs.static;
        } else {
          dataConfigs[key] = {
            fields: component.dataConfigs[key].fields,
            config: component.dataConfigs[key].config,
          };
        }
      }
      return {
        ...component,
        dataConfigs: dataConfigs,
      };
    };

    const componentsChildren: any = [];
    const components = componentIds.map((id) => {
      const component = getSimpleComponent(componentsById[id]);
      const children =
        component.children &&
        component.children.map((childId) => getSimpleComponent(componentsById[childId]));
      if (children) {
        componentsChildren.push(...children);
      }
      return component;
    });
    const panels = panelIds.map((d) => panelsById[d]);

    // const selectedLayers = filterSelectedLayers(layersSelected, layers);
    return {
      id,
      components,
      children: componentsChildren,
      panels,
      originLayers: layers,
      selected: layersSelected,
    };
  };
}

// 粘贴组件
export function pasteComponents(to = 'screen', center) {
  return async (dispatch, getState) => {
    const { createApp } = getState();
    const { id: screenId, scale } = createApp;

    const copyInfoString: string | null = localStorage.getItem('easyv_component');

    if (!copyInfoString) {
      return;
    }
    let copyInfo: any;
    // 尝试解析剪切板内容
    try {
      copyInfo = JSON.parse(copyInfoString);
    } catch (e) {
      // 解析失败什么都不做
      return;
    }

    const { from, data } = copyInfo;
    if (from === to) {
      const hide = message.loading('正在粘贴组件...', 0);
      const { id, originLayers, selected, components, children, panels } = data;

      let componentPositions;
      let panelPositions;
      if (center) {
        const screenCenter = getScreenCenter(scale);
        const componentDimensions = components.map((d) => getComponentDimension(d.config));
        const panelDimensions = panels.map((d) => d.config);
        const originCenter = getCenterFromDimension(componentDimensions.concat(panelDimensions));
        const translateX = screenCenter.left - originCenter[0];
        const translateY = screenCenter.top - originCenter[1];

        if (translateX !== 0 || translateY !== 0) {
          componentPositions = components.map((d) => {
            const dimension = getComponentDimension(d.config);

            return {
              left: dimension.left + translateX,
              top: dimension.top + translateY,
            };
          });

          panelPositions = panels.map((d) => ({
            left: d.config.left + translateX,
            top: d.config.top + translateY,
          }));
        }
      }
      hide();
      await dispatch(
        copyLayersTo(
          {
            layers: originLayers,
            selected: selected,
            components: components,
            children: children,
            panels: panels,
            screenId: id,
            componentPositions,
            panelPositions,
          },
          screenId,
        ),
      );
    } else {
      message.error(
        `不能将${from === 'screen' ? '大屏' : '终端交互'}组件复制到${
          to === 'screen' ? '大屏' : '终端交互'
        }`,
      );
    }
  };
}

// 粘贴组件样式
export function pasteStyle() {
  return async (dispatch, getState) => {
    const { createApp, componentsById } = getState();
    const { layersSelected, componentsChildSelected } = createApp;
    if (!layersSelected.length) {
      message.error('请先选择组件，再进行样式粘贴');
      return;
    }

    if (layersSelected.some((d) => !isComponent(d))) {
      message.error('只能粘贴样式到组件');
      return;
    }

    const componentIds = componentsChildSelected ? [componentsChildSelected] : layersSelected;
    const components = componentIds.map((d) => componentsById[d]);
    if (!components.every((d) => d)) {
      message.error('组件存在丢失，请刷新后重试');
      return;
    }

    const styleString = sessionStorage.getItem('component_style');

    if (!styleString) {
      return;
    }

    let style: any;

    // 尝试解析剪切板内容
    try {
      style = JSON.parse(styleString);
    } catch (e) {
      message.error('组件样式解析失败');
      return;
    }

    if (!style) {
      message.error('请先复制组件样式');
      return;
    }

    if (components.some((d) => d.base.module_name !== style.moduleName)) {
      message.error('仅限同类型组件进行样式复制');
      return;
    }

    const hide = message.loading('正在粘贴样式', 0);
    const newConfigs = components.map((d) => ({
      componentId: d.id,
      updateData: {
        config: mergeStyleConfig(d.config, style.config),
      },
    }));

    const result = await dispatch(updateComponentConfig(newConfigs));

    hide();
    if (result) {
      message.success('样式粘贴成功');
    }
  };
}

// 复制选中图层到指定屏幕
export function copySelectedLayerToScreen(screenId) {
  return async (dispatch) => {
    dispatch({
      type: actionTypes.CROSS_COPY_STATR,
    });
    try {
      const copyComponentsInfo = dispatch(getSelectedComponentsInfo());
      const { id, components, children, panels, selected, originLayers } = copyComponentsInfo;
      if (!copyComponentsInfo) {
        return;
      }
      const result = await dispatch(
        copyLayersTo(
          { layers: originLayers, selected, components, children, panels, screenId: id },
          screenId,
        ),
      );
      if (result) {
        message.success('复制成功');
        dispatch({
          type: actionTypes.CROSS_COPY_SUCCESS,
        });
      } else {
        dispatch({
          type: actionTypes.CROSS_COPY_FAILURE,
        });
      }
    } catch (error) {
      console.error(error);
      message.error('复制失败');
      dispatch({
        type: actionTypes.CROSS_COPY_FAILURE,
      });
    }
  };
}

// 复制图层到指定屏幕
// layers 只是为了传给后端
function copyLayersTo(
  {
    layers,
    selected,
    components,
    componentPositions,
    children,
    panels,
    panelPositions,
    screenId,
  }: {
    layers: any;
    selected: any;
    components: any;
    componentPositions?: any;
    children: any;
    panels: any;
    panelPositions?: any;
    screenId: any;
  },
  toScreenId: number,
) {
  return async (dispatch, getState) => {
    const { createApp, screensById } = getState();
    const { id: currentScreenId, layersSelected } = createApp;

    const firstSelectedId =
      toScreenId === currentScreenId
        ? getFirstSelectedId({
            layers: screensById[currentScreenId].layers,
            selected: layersSelected,
          })
        : undefined;

    const hide = message.loading('正在复制组件...', 0);

    const result: any = await http.post<{
      components: ComponentItem[];
      children: ComponentItem[];
      layers: Layers;
      selected: Layers;
      panels: Panel[];
      newFilters: Filters;
    }>(api.componentCopy, {
      id: screenId,
      aimId: toScreenId,
      components: components.map((o) => o.id),
      children: children.map((o) => o.id),
      originLayers: layers,
      panels: panels,
      insertId: firstSelectedId,
      selected,
      componentPositions,
      panelPositions,
    });

    hide();
    if (result && currentScreenId === toScreenId) {
      const {
        components: newComponents,
        children: newChildren,
        layers: newLayers,
        selected: newSelected,
        panels: newPanels,
        newFilters = [],
      } = result;

      const addedComponents = components
        .map((item, i) => {
          return {
            ...item,
            id: newComponents[i].id,
            events: newComponents[i].events,
            uniqueTag: newComponents[i].uniqueTag,
            screenId: toScreenId,
            config: newComponents[i].config,
            filters: newComponents[i].filters,
            children: newChildren.filter((d) => d.parent === newComponents[i].id).map((d) => d.id),
          };
        })
        .concat(
          children.map((item, i) => ({
            ...item,
            id: newChildren[i].id,
            uniqueTag: newChildren[i].uniqueTag,
            screenId: toScreenId,
            events: newChildren[i].events,
            config: newChildren[i].config,
            parent: newChildren[i].parent,
            filters: newChildren[i].filters,
          })),
        );
      // 更新过滤器
      dispatch({
        type: actionTypes.FILTER_ADD,
        payload: {
          filter: newFilters.map((d) => ({ ...d, screenId: toScreenId })),
        },
      });

      // dispatch({
      //   type: actionTypes.COMPONENTS_ADD,
      //   payload: { list: addedComponents }
      // });
      dispatch(commponentsAdd({ list: addedComponents }));

      dispatch({
        type: actionTypes.PANEL_COPY,
        payload: newPanels,
      });

      newPanels.forEach((d) => {
        d.states.forEach((o) => dispatch(getScreen(o)));
      });

      // 更新新的layers
      dispatch({
        type: actionTypes.LAYER_UPDATE,
        payload: {
          id: currentScreenId,
          layers: newLayers,
        },
      });

      // 选中复制后的所有图层
      dispatch({
        type: actionTypes.LAYER_SELECT,
        payload: {
          selected: newSelected.map((d) => d.id),
        },
      });
    }

    return result;
  };
}

// 同步组件信息
export function componentDataSyanc(id) {
  return async (dispatch, getState) => {
    const { user, componentsById, panelsById } = getState();
    const isComponent = !/(panel|ref)_[0-9]+/.test(id);

    const { nonentityModule } = user;
    const isExist = componentsById[id] || panelsById[id];

    if (nonentityModule.includes(id) || ~isExist) {
      // 之前查过没查到 或者当前数据正常 都拦住
      return;
    }
    const _id = !isComponent && (getId(id) as number);
    const result: any =
      typeof id == 'number'
        ? await http.get(
            api.componentDataSyanc,
            { id },
            {
              error: () => {
                //
              },
            },
          )
        : !isComponent
        ? await dispatch(getPanelInfoForScreenEdit(_id))
        : null;
    if (typeof result === 'object' && result) {
      if (isComponent) {
        // dispatch({
        //   type: actionTypes.COMPONENTS_ADD,
        //   payload: {
        //     ...result,
        //     dataConfigs: {
        //       static: result.staticData
        //     }
        //   }
        // });
        dispatch(
          commponentsAdd({
            ...result,
            dataConfigs: {
              static: result.staticData,
            },
          }),
        );
      }
    } else {
      dispatch(updateNonentityModule(id));
    }
  };
}

// 将选中图层转换为panel
export function transformLayersToPanel() {
  return async (dispatch) => {
    const componentsInfo = dispatch(getSelectedComponentsInfo());
    if (!componentsInfo) {
      return;
    }
    const hide = message.loading('正在转换为动态面板...', 0);
    let { components, panels } = componentsInfo;
    const { id, children, originLayers, selected } = componentsInfo;
    const componentDimensions = components.map((d) => getComponentDimension(d.config));
    const panelDimensions = panels.map((d) => d.config);
    const { left, top, width, height } = getDimensionFromChildren(
      componentDimensions.concat(panelDimensions),
    );
    components = components.map((d) => {
      let config = d.config;
      const dimension = getComponentDimension(d.config);

      config = updateArrayConfig(config, [
        { path: ['chart', 'dimension', 'chartPosition', 'left'], value: dimension.left - left },
        { path: ['chart', 'dimension', 'chartPosition', 'top'], value: dimension.top - top },
      ]);

      return {
        ...d,
        config: config,
      };
    });

    panels = panels.map((d) => ({
      ...d,
      config: {
        ...d.config,
        left: d.config.left - left,
        top: d.config.top - top,
      },
    }));

    const result: any = await http.post<{
      layers: Layers;
      selected: {
        id: string;
      };
    }>(api.panelTransform, {
      id,
      components: components.map(transformComponentToServer),
      children: children.map(transformComponentToServer),
      panels,
      originLayers,
      selected,
      panelConfig: {
        left,
        top,
        width: width,
        height: height,
      },
    });

    hide();
    if (result) {
      const { layers: newLayers, selected } = result;

      const resultPanelId = selected.id;
      await dispatch(getPanelInfoForScreenEdit(getId(resultPanelId)));

      // 更新新的layers
      dispatch({
        type: actionTypes.LAYER_UPDATE,
        payload: {
          id: id,
          layers: newLayers,
        },
      });

      dispatch({
        type: actionTypes.LAYER_SELECT,
        payload: {
          selected: [resultPanelId],
        },
      });

      message.success('转换成功');
    }
  };
}

// 更新弹窗组件配置 - 也可以支持不弹窗组件
export function updateModalConfig({ id, config: param }) {
  return async (dispatch, getState) => {
    const { componentsById } = getState();

    const component = componentsById[id];

    if (!component) {
      message.error('更新的组件不存在');
      return;
    }

    dispatch(addUndoConfig({ list: [component] }));

    param = Array.isArray(param) ? param : [param];

    const config = updateArrayConfig(component.config, param);

    const newComponent = {
      ...component,
      config: config,
    };

    dispatch({
      type: actionTypes.COMPONENT_UPDATE,
      payload: { list: [newComponent] },
    });

    const screenId = getScreenId();
    if (!screenId) {
      message.error('大屏id 不存在');
      return;
    }

    const result = await http.post(api.componentUpdate, {
      screenId,
      configs: [
        {
          id,
          parent: component.parent,
          config: config,
        },
      ],
    });

    return result;
  };
}

export function updateModalData({ id, data }) {
  return async (dispatch, getState) => {
    if (!id || !data) {
      return;
    }

    const { componentsById } = getState();

    const component = componentsById[id];
    dispatch(addUndoConfig({ list: [component] }));

    const newComponent = {
      ...component,
      dataConfigs: {
        ...component.dataConfigs,
        static: {
          data: data,
          fields: component.dataConfigs.fields,
        },
      },
    };

    dispatch({
      type: actionTypes.COMPONENT_UPDATE,
      payload: { list: [newComponent] },
    });

    const result = await http.post(api.componentDataConfigUpdate, {
      id: id,
      type: 'static',
      data: data,
    });

    return result;
  };
}

// 更新自定义动作的数据
export function updateAction({
  id,
  actions: param,
  isUpdateAllConfig,
}: {
  id: string;
  actions: any;
  isUpdateAllConfig: boolean;
}) {
  return async (dispatch, getState) => {
    const { componentsById } = getState();

    const component = componentsById[id];
    if (!component) {
      return;
    }

    const { actions = [] } = component;

    param = Array.isArray(param) ? param : [param];

    /**
     * @description: 过滤actions 脏数据
     */
    if (isUpdateAllConfig) {
      param = param.filter((d) => d && d.name && d.value);
    }

    const newActions = isUpdateAllConfig
      ? param
      : actions.map((d) => {
          const currentActionParam = param.filter((o) => o.actionName === d.value);
          const newConfig = currentActionParam.reduce(
            (all, cur) =>
              updateArrayConfig(all, { path: cur.path, value: cur.value, field: cur.field }),
            d.config,
          );
          return {
            ...d,
            config: newConfig,
          };
        });
    const newComponent = {
      ...component,
      actions: newActions,
    };

    dispatch({
      type: actionTypes.COMPONENT_UPDATE,
      payload: { list: [newComponent] },
    });
    const screenId = getScreenId();
    if (!screenId) {
      message.error('大屏id 不存在');
      return;
    }
    const result = await http.post(api.componentUpdate, {
      screenId,
      configs: [
        {
          id,
          parent: component.parent,
          actions: newActions,
        },
      ],
    });

    return result;
  };
}

// 更新主题config配置项
export function updateComponentsThemeConfig(list) {
  return async (dispatch) => {
    dispatch({
      type: actionTypes.THEME_COMPONENT_UPDATE,
      payload: { list },
    });
  };
}

// 回滚主题config配置项
export function resetComponentsThemeConfig() {
  return async (dispatch, getState) => {
    const { themes } = getState();
    const { themeComponentById } = themes;
    if (themeComponentById && Object.keys(themeComponentById).length) {
      await dispatch({
        type: actionTypes.THEME_COMPONENT_UPDATE,
        payload: { list: Object.values(themeComponentById) },
      });
    }
    await dispatch(resetScreenConfig());
    dispatch(clearThemeComponentBackUp());
  };
}

// 更新组件component_config里的内容
export function updateComponentConfig(list) {
  return async (dispatch, getState) => {
    const { componentsById } = getState();

    if (isPlainObject(list)) {
      list = [list];
    }

    const newStates = list.map(({ componentId, updateData }) =>
      deepUpdate(updateData, componentsById[componentId]),
    );
    const oldStates = list.map(({ componentId }) => componentsById[componentId]);

    dispatch(addUndoConfig({ list: oldStates }));

    dispatch({
      type: actionTypes.COMPONENT_UPDATE,
      payload: { list: newStates },
    });

    const configs = list.map((item) => {
      const { componentId, updateData } = item;
      const parent = componentsById[componentId].parent;
      let fields;
      if (updateData.dataConfigs) {
        fields = Object.keys(updateData.dataConfigs).reduce((all, current) => {
          all[current] = updateData.dataConfigs[current].fields;
          return all;
        }, {});
      }
      const base = updateData.base && {
        module_name: updateData.base.module_name,
        version: updateData.base.version,
      };

      return {
        id: componentId,
        parent,
        name: updateData.name,
        base: base,
        config: updateData.config,
        fields,
        actions: updateData.actions,
        triggers: updateData.triggers,
      };
    });
    const screenId = getScreenId();
    if (!screenId) {
      message.error('大屏id 不存在');
      return;
    }
    const result = await http.post(api.componentUpdate, {
      screenId,
      configs: configs,
    });
    return result;
  };
}

export function synchronizationTriggers(triggers, componentId) {
  return async (dispatch, getState) => {
    const { componentsById } = getState();
    const component = componentsById[componentId];
    if (component) {
      dispatch({
        type: actionTypes.COMPONENT_UPDATE,
        payload: {
          ...component,
          triggers,
        },
      });
    }
  };
}

export function updateTriggers({ triggers, id: componentId }) {
  return async (dispatch) => {
    const screenId = getScreenId();
    if (!screenId) {
      message.error('大屏id 不存在');
      return;
    }
    const result = await http.post(api.componentUpdate, {
      screenId,
      configs: [
        {
          id: componentId,
          triggers: triggers,
        },
      ],
    });
    result && dispatch(synchronizationTriggers(triggers, componentId));
    return result;
  };
}

// 更新组件数据页信息
export function updateComponentData({ componentId, updateData }) {
  return (dispatch, getState) => {
    const { componentsById } = getState();
    const screenId = getScreenId();
    const newState: any = deepUpdate(updateData, componentsById[componentId]);

    const { dataType, dataConfigs, useFilter, filters, autoUpdate } = newState;
    const dataConfig = dataConfigs[dataType];

    dispatch({
      type: actionTypes.COMPONENT_DATA_UPDATE,
      payload: { list: [newState] },
    });

    http
      .post(api.componentDataChange, {
        screen_id: screenId,
        component_id: componentId,
        type: dataType,
        component: {
          component_data_config: {
            data: dataType === 'static' ? dataConfig.data : undefined,
            data_config: dataConfig.config,
            fields: dataConfig.fields,
            useFilter,
            filters: filters,
            autoUpdate: autoUpdate,
          },
        },
      })
      .then(({ component_data }) => {
        // only update data
        const { type: dataType, config } = component_data;
        dispatch(refreshDynamicData({ componentId, dataType, data: config.data }));
      })
      .catch((error) => {
        console.error(error);
        message.error('修改失败');
      });
  };
}

// 更新component和panel的config信息
export function hybridUpdate({ component = [], panel = [] }) {
  return async (dispatch, getState) => {
    const { componentsById, panelsById } = getState();

    let components = component;
    let panels = panel;
    !Array.isArray(components) && (components = [components]);
    !Array.isArray(panel) && (panels = [panel]);

    const nextComps: any = components.map(({ componentId, updateData }) =>
      deepUpdate(updateData, componentsById[componentId]),
    );
    const oldComps = components.map(({ componentId }) => componentsById[componentId]);
    const nextPanels: any = panels.map(({ id, updateData }) =>
      deepUpdate(updateData, panelsById[id]),
    );
    const oldPanels = panels.map(({ id }) => ({ ...panelsById[id], id: `panel_${id}` }));

    dispatch(addUndoConfig({ list: oldComps.concat(oldPanels) }));
    dispatch({
      type: actionTypes.HYBRID_UPDATE,
      payload: { panels: nextPanels, components: nextComps },
    });
    const screenId = getScreenId();
    if (nextComps.length && screenId) {
      http.post(api.componentUpdate, {
        screenId,
        configs: nextComps.map((item) => ({
          id: item.id,
          name: item.name,
          base: {
            module_name: item.base.module_name,
            version: item.base.version,
          },
          config: item.config,
        })),
      });
    }

    if (nextPanels.length) {
      http.post(api.panelUpdate, {
        configs: nextPanels.map((item) => ({
          id: item.id,
          config: JSON.stringify(item.config),
        })),
      });
    }
  };
}

// 修改组件数据源类型
export function changeDataSourceType(type: ComponentItem['dataType']) {
  return async (dispatch, getState) => {
    const { createApp, componentsById } = getState();
    const { layersSelected, componentsChildSelected } = createApp;
    const componentId = componentsChildSelected || layersSelected[0];

    // 如果有数据请求，则取消
    signalStore[componentId] && signalStore[componentId].abort();

    const result: any = await http.post<{
      baseURL: string;
    }>(api.componentSourceChange, {
      id: componentId,
      type: type,
    });

    if (result) {
      const { dataConfigs } = componentsById[componentId];
      const param: any = {
        id: componentId,
        dataType: type,
        errorMessage: null,
      };
      if (type === 'api' && result.baseURL) {
        param.baseURL = result.baseURL;
      }

      if (!dataConfigs[type]) {
        param.dataConfigs = {
          ...dataConfigs,
          [type]: {
            fields: dataConfigs.static.fields,
            data: [],
          },
        };
      }
      dispatch({
        type: actionTypes.COMPONENT_DATA_CONFIG_UPDATE,
        payload: param,
      });

      // 切换数据源，如果不是静态数据的话，需要请求数据
      if (type !== 'static') {
        if (!dataConfigs[type] || !dataConfigs[type].data || !dataConfigs[type].data.length) {
          dispatch(getComponentData(componentId));
        }
      }
    }

    return result;
  };
}

// 更新数据容器/API 切换
export function updateUseDataContainer(id: number, dataFrom: 0 | 1) {
  return async (dispatch) => {
    const screenId = getScreenId();
    if (!screenId) {
      message.error('大屏id 不存在');
      return;
    }
    const result = await http.post(api.componentUpdate, {
      screenId,
      configs: [
        {
          id,
          dataFrom,
        },
      ],
    });
    result &&
      dispatch({
        type: actionTypes.COMPONENT_DATA_CONTAINER_CONFIG_UPDATE,
        payload: {
          id,
          dataFrom,
        },
      });
    return result;
  };
}

export function resetComponent(list: any[]) {
  //  恢复组件
  return async (dispatch, getState) => {
    const { componentsById } = getState();
    // eslint-disable-next-line @typescript-eslint/ban-types
    const newList: any = list.map(
      (e: {
        parent: null | number;
        staticData: object;
        dataConfigs: object;
        id: number;
        dataType: string;
      }) => {
        let item: any = {
          ...e,
          signal: {
            aborted: false, // 请求
            onabort: null,
          },
          children: !e.parent
            ? list
                .filter((v) => {
                  return v.parent == e.id;
                })
                .map((item) => {
                  return item.id;
                })
            : undefined,
        };
        item = componentOperator.createFromServer(item);
        return item;
      },
    );

    const newListKv = {};

    newList
      .filter((e) => {
        return e.parent;
      })
      .forEach((value) => {
        newListKv[value.parent] = value;
      });

    const comList: any[] = [];
    Object.values(componentsById).forEach((v: { id: number; children: any[] }) => {
      if (newListKv[v.id] && newListKv[v.id].id) {
        comList.push({
          ...v,
          children: Array.from(new Set([newListKv[v.id].id, ...v.children])),
        });
      }
    });
    // await dispatch({
    //   type: actionTypes.COMPONENTS_ADD,
    //   payload: { list: [...newList, ...comList] }
    // });
    await dispatch(commponentsAdd({ list: [...newList, ...comList] }));
    for (let i = 0; i < newList.length; i++) {
      if (newList[i].dataType !== 'static') {
        dispatch(getComponentData(newList[i].id));
      }
    }
  };
}

// 添加组件
export function addComponent({
  moduleName,
  version,
  name,
  position,
  updates = [],
  configuration: _configuration,
  isTenant,
}: {
  moduleName: string;
  version: string;
  name: string;
  configuration?: any;
  position?: { x: number; y: number };
  updates?: any[];
  isTenant?: boolean;
}) {
  return async (dispatch, getState) => {
    const hide = message.loading('正在添加组件...', 0);
    const { createApp, themes, screensById, user } = getState();
    const { userInfo } = user;
    //获取组件
    const component: any = await getComponent(
      moduleName,
      version,
      isTenant ? userInfo.tenantId : undefined,
    );
    if (component) {
      const { id, layersSelected, scale } = createApp;
      const { list: themeList = [] } = themes;
      const screen = screensById[id];
      const { layers } = screen;

      const defaultConfig = component.getDefaultConfig();
      const defaultData = component.getDefaultData();
      const configuration = _configuration || defaultConfig?.configuration;
      const {
        base = {},
        actions = [],
        isTrigger = false,
        triggers = [],
        defaultChildren = [],
      } = defaultConfig;
      let config = configuration;

      const childrenDefaultConfig =
        component.getDefaultChildrenConfig && component.getDefaultChildrenConfig();
      const childrenDefaultData =
        component.getDefaultChildrenData && component.getDefaultChildrenData();
      let children: any = [];
      if (
        childrenDefaultConfig &&
        childrenDefaultData &&
        childrenDefaultConfig.length > 0 &&
        childrenDefaultData.length > 0
      ) {
        let defaultAddedChildren: any = [];
        if (defaultChildren.length > 0) {
          defaultAddedChildren = defaultChildren.map((d) =>
            typeof d === 'string' ? { moduleName: d } : d,
          );
        } else {
          defaultAddedChildren = getConfig(config, 'children').default;
        }

        children = defaultAddedChildren.map((addedChild) => {
          const {
            id: childRandomId,
            base: childBase = {},
            configuration: childConfiguration = [],
            actions: childActions = [],
            triggers: childTriggers = [],
          } = childrenDefaultConfig.find((d) => d.base.module_name === addedChild.moduleName);
          const childDefaultData = childrenDefaultData.find((d) => d.id === childRandomId);

          return {
            name: childBase.name,
            base: {
              module_name: childBase.module_name,
              version: childBase.version,
              name: childBase.name,
            },
            config: childConfiguration,
            staticData: {
              data: childDefaultData.data,
              fields: childDefaultData.fields,
            },
            triggers: childTriggers,
            actions: childActions,
          };
        });
      }

      // 如果组件存在默认主题，则组件配置需将本身配置与主题配置合并
      const hasDefaultTheme = themeList.find(
        (theme) => theme.isDefault && theme.from === moduleName,
      );
      if (hasDefaultTheme) {
        const componentDefaultThemes = themeList.filter(
          (theme) => theme.from === moduleName && theme.name === hasDefaultTheme.name,
        );
        if (componentDefaultThemes.length) {
          const themeIds = componentDefaultThemes.map((d) => d.id);

          let defaultThemeDetails: any = [];

          if (window.appConfig.LOCAL || userInfo?.edition.supportTheme) {
            defaultThemeDetails = await http.get<ThemeDetail[]>(api.themeList, {
              ids: themeIds,
              config: true,
            });
          }
          if (defaultThemeDetails && defaultThemeDetails.length >= 1) {
            // 父组件样式应用主题
            const parentDefaultTheme = defaultThemeDetails.find(
              (theme) => theme.moduleName === moduleName,
            );
            config = parentDefaultTheme
              ? mergeStyleConfig(config, parentDefaultTheme.config)
              : config;

            // 子组件样式应用主题
            children = children.map((child) => {
              const childTheme = defaultThemeDetails.find(
                (theme) => theme.moduleName === child.base.module_name,
              );
              const childConfig = childTheme
                ? mergeStyleConfig(child.config, childTheme.config)
                : child.config;

              return {
                ...child,
                config: childConfig,
              };
            });
          }
        }
      }

      // 如果组件是点击添加，则组件在当前视图中间显示，否则以传过来的位置显示
      let posX, posY;
      if (position) {
        const simulatorDom = document.getElementById('m-simulator');
        const { left = 0, top = 0 } = simulatorDom?.getBoundingClientRect() || {};
        posX = Math.round((position.x - left) / scale);
        posY = Math.round((position.y - top) / scale);
      } else {
        const { left, top } = getScreenCenter(scale);
        posX = left;
        posY = top;
      }

      const { width, height } = getComponentDimension(config);
      config = updateArrayConfig(config, [
        {
          path: ['chart', 'dimension', 'chartPosition', 'left'],
          value: Math.round(posX - width / 2),
        },
        {
          path: ['chart', 'dimension', 'chartPosition', 'top'],
          value: Math.round(posY - height / 2),
        },
        ...updates,
      ]);

      if (isTrigger) {
        config[0]['isTrigger'] = true;
      }

      // 获取图层中第一个被选中的图层id
      const firstSelectedId = getFirstSelectedId({ layers, selected: layersSelected });

      const _body = {
        screenId: id,
        component: {
          name: name,
          base: {
            module_name: base.module_name,
            version: base.version,
          },
          config: config,
          staticData: defaultData,
          triggers: triggers,
          actions: actions,
        },
        children: children,
        insertId: firstSelectedId,
      };
      const result = await request(api.componentAdd, undefined, undefined, JSON.stringify(_body));

      hide();
      if (result) {
        /** 组件使用信息埋点
         * componentId => 组件id
         * componentVersion => 组件版本
         * componentName => 组件名
         * screenId => 大屏id
         * createTime => 创建时间
         * module_name => 组件类型
         * componentUseNum => 使用次数
         * */
        dispatch(
          eventTracking<CollectDataTs>({
            type: 'component',
            id: result.component.id,
            componentId: result.component.id,
            componentVersion: _body.component.base.version,
            screenId: _body.screenId,
            module_name: _body.component.base.module_name,
            componentName: _body.component.name,
            componentUseNum: 1,
          }),
        );

        const { component: newComponent, children: newChildren } = result;
        const newState = [
          {
            id: newComponent.id,
            name: name,
            uniqueTag: newComponent.uniqueTag,
            base,
            config: config,
            dataConfigs: {
              static: defaultData,
            },
            dataContainers: [],
            dataFrom: DataFrom.DEFAULT,
            dataType: 'static',
            parent: null,
            autoUpdate: {
              isAuto: false,
              interval: 60,
            },
            from: isTenant ? 1 : 0,
            filters: [],
            useFilter: false,
            children: newChildren.map((d) => d.id),
            screenId: id,
            triggers: triggers,
            actions: actions,
            events: [],
          },
        ].concat(
          newChildren.map((current, i) => {
            const child = children[i];
            if (isTrigger && child.config) {
              try {
                child.config[0]['isTrigger'] = true;
              } catch (err) {
                console.log(err);
              }
            }
            return {
              id: current.id,
              name: child.name,
              uniqueTag: current.uniqueTag,
              base: child.base,
              config: child.config,
              dataConfigs: {
                static: child.staticData,
              },
              dataContainers: [],
              dataFrom: DataFrom.DEFAULT,
              dataType: 'static',
              parent: newComponent.id,
              autoUpdate: {
                isAuto: false,
                interval: 60,
              },
              filters: [],
              useFilter: false,
              screenId: id,
              triggers: child.triggers,
              actions: child.actions,
              events: [],
            };
          }),
        );
        // 将组件信息添加到已用组件中
        // dispatch({
        //   type: actionTypes.COMPONENTS_ADD,
        //   payload: { list: newState }
        // });
        dispatch(commponentsAdd({ list: newState }));

        // 更新图层信息
        const newLayers = insertLayerById({
          layers,
          layer: {
            id: newComponent.id,
            name,
            moduleName: moduleName,
            show: true,
            lock: false,
          },
          insertId: firstSelectedId,
        });

        dispatch({
          type: actionTypes.LAYER_UPDATE,
          payload: {
            id,
            layers: newLayers,
          },
        });

        dispatch(selectLayerWithScrollIntoView(newComponent.id));

        return result;
      }
    }
    hide?.();
  };
}

// 添加子组件
export function addChildComponent({ componentId, childModuleName }) {
  return async (dispatch, getState) => {
    const hide = message.loading('正在添加...', 0);
    const { createApp, componentsById, user } = getState();
    const { id: screenId } = createApp;
    const comp = componentsById[componentId];

    const { module_name, version } = comp.base;
    let component = window.component && window.component[module_name + '@' + version];

    // 如果父组件未加载则加载其组件
    if (!component) {
      component = await getComponent(
        module_name,
        version,
        comp.from === 1 ? user.userInfo.tenantId : undefined,
      );
    }

    if (component) {
      const childrenConfigs = component.getDefaultChildrenConfig();
      const childrenDatas = component.getDefaultChildrenData();
      const childConfig = childrenConfigs.find((item) => item.base.module_name === childModuleName);

      const { id, base, configuration, actions = [], triggers = [] } = childConfig;
      const dataConfig = childrenDatas.find((item) => item.id === id) || {};

      const child = {
        name: base.name,
        base: {
          module_name: base.module_name,
          version: base.version,
          name: base.name,
        },
        config: configuration,
        staticData: {
          data: dataConfig.data,
          fields: dataConfig.fields,
        },
        // dataContainers: [],
        // dataFrom: DataFrom.DEFAULT,
        actions: actions,
        triggers: triggers,
      };

      const result = await request(
        api.componentAdd,
        undefined,
        undefined,
        JSON.stringify({
          screenId,
          component: componentId,
          children: [child],
        }),
      );

      hide();
      if (result) {
        message.success('添加成功');

        const { children: newChildren } = result;
        const newState = newChildren.map((d) => ({
          id: d.id,
          name: child.name,
          uniqueTag: d.uniqueTag,
          base: child.base,
          dataContainers: d.dataContainers || [],
          dataFrom: d.dataFrom || DataFrom.DEFAULT,
          config: child.config,
          dataConfigs: {
            static: child.staticData,
          },
          dataType: 'static',
          parent: componentId,
          useFilter: false,
          filters: [],
          autoUpdate: {
            isAuto: false,
            interval: 60,
          },
          actions: child.actions,
          triggers: child.triggers,
          events: [],
          screenId: screenId,
        }));

        // 将组件信息添加到已用组件中
        // dispatch({
        //   type: actionTypes.COMPONENTS_ADD,
        //   payload: { list: newState }
        // });
        dispatch(commponentsAdd({ list: newState }));

        dispatch({
          type: actionTypes.COMPONENT_UPDATE,
          payload: {
            list: [
              {
                ...comp,
                children: comp.children
                  ? comp.children.concat(newChildren.map((d) => d.id))
                  : newChildren.map((d) => d.id),
              },
            ],
          },
        });

        return result;
      }
    }
  };
}

function getTree(tree: [], fun: (id: number | string) => void) {
  tree &&
    tree.length &&
    tree.forEach((data: { components: any; id: number | string }) => {
      fun(data.id);
      data.components && getTree(data.components, fun);
    });
}

// 删除所有选中的图层
export function deleteComponents() {
  return async (dispatch, getState) => {
    const hide = message.loading('正在删除...', 0);
    const { createApp, componentsById, screensById, panelsById } = getState();
    const { layersSelected, componentsChildSelected, id: screenId } = createApp;
    const layers = screensById[screenId].layers;

    const layersParam: any = [];
    let newLayersSelected = typeof layersSelected != 'string' ? layersSelected : [layersSelected];

    newLayersSelected = newLayersSelected.filter((e: string | number) => {
      const code = typeof e === 'string' && e.indexOf('group_') > -1;
      if (code) {
        const item = layers.filter((item: { id: string }) => {
          return item.id === e;
        });

        // start 抽取当前涂层所有id，然后去和componentsById比对，若存在拿出children
        const childrenIds: any[] = [],
          children: any[] = [];
        item && item.length
          ? getTree(item[0].components, (id: string | number) => {
              childrenIds.push(id);
            })
          : null;
        childrenIds.forEach((c) => {
          componentsById[c] && children.push(...(componentsById[c].children || []));
        });
        layersParam.push({
          id: e,
          children: children,
        });
      } else {
        return e;
      }
    });
    // end

    const components = screenOperator.getComponentsFromSelectedLayers(newLayersSelected, layers);
    // 组件的子组件
    const children: any = [];
    components.forEach((id) => {
      const _children = componentOperator.getChildren(id, componentsById).map((item) => item.id);
      layersParam.push({
        id,
        children: children ? children.concat(_children) : [],
      });
    });

    const result = await request(
      api.componentDelete,
      undefined,
      undefined,
      JSON.stringify({
        screenId: screenId,
        layers: layersParam,
      }),
    );

    if (result) {
      const { layers: newLayers, recycleItems } = result;
      // 更新layers
      dispatch({
        type: actionTypes.LAYER_UPDATE,
        payload: {
          id: screenId,
          layers: newLayers,
        },
      });
      // 更新回收站
      dispatch(addRecycleBin(recycleItems));
      message.success('删除成功');
      //取消选择的图层
      dispatch({
        type: actionTypes.SELECT_RESET,
      });
      if (componentsChildSelected) {
        dispatch({
          type: actionTypes.RESET_CHILD_SELECTED,
        });
      }
    }

    hide();

    const panelIds: any = [];
    const compIds: any = [];
    function loop(list, callback) {
      list.forEach((id) => {
        if (isGeneralPanel(id)) {
          const panelId: any = getId(id);
          callback('panel', panelId);
          const panel = panelsById[panelId];
          panel &&
            panel.states.map((screenId) =>
              loop(screenOperator.getComponentsFromLayers(screensById[screenId].layers), callback),
            );
        } else {
          callback('component', id);
        }
      });
    }
    loop(components, (type, id) => {
      if (type === 'panel') panelIds.push(id);
      else {
        compIds.push(id);
        componentOperator.getChildren(id, componentsById).forEach((item) => compIds.push(item.id));
      }
    });
    // 删除所有嵌套panel
    panelIds.length && dispatch({ type: actionTypes.PANEL_DELETE, payload: { list: panelIds } });
    // 删除所有嵌套component(group/children/panel)
    // compIds.length &&
    //   dispatch({
    //     type: actionTypes.COMPONENTS_DELETE,
    //     payload: { list: compIds }
    //   });
  };
}

// 更新选中图层的尺寸位置
export function updateSizePositionForSelectedLayers({
  scaleWidth = 1,
  scaleHeight = 1,
  deltaX = 0,
  deltaY = 0,
  x,
  y,
  originX,
  originY,
  isAspectFixed = false,
}) {
  if (isAspectFixed) {
    if (scaleWidth !== 1) {
      scaleHeight = scaleWidth;
    } else if (scaleHeight !== 1) {
      scaleWidth = scaleHeight;
    }
  }
  return (dispatch, getState) => {
    const { createApp, panelsById, componentsById, screensById } = getState();
    const { layersSelected, id: screenId } = createApp;
    const screen = screensById[screenId];

    const [groupX, groupY] = [isNumber(originX) ? originX : x, isNumber(originY) ? originY : y];
    const componentStates: any = [];
    const panelStates: any = [];
    function handleSizePosition(target) {
      const { left, top, width, height } = target;
      if (scaleWidth !== 1 || scaleHeight !== 1) {
        return {
          left: groupX + Math.round((left - groupX) * scaleWidth),
          top: groupY + Math.round((top - groupY) * scaleHeight),
          width: Math.round(width * scaleWidth),
          height: Math.round(height * scaleHeight),
        };
      } else {
        return { ...target, left: Math.round(left + deltaX), top: Math.round(top + deltaY) };
      }
    }
    screenOperator.getComponentsFromSelectedLayers(layersSelected, screen.layers).forEach((id) => {
      if (isGeneralPanel(id)) {
        const panelId: any = getId(id);
        panelStates.push({
          id: panelId,
          updateData: {
            config: handleSizePosition(panelsById[panelId].config),
          },
        });
      } else {
        const component = componentsById[id];
        const dimension = getComponentDimension(component.config);
        const { width, height, top, left } = handleSizePosition(dimension);
        let _configs = component.config;
        _configs = componentOperator.scaleTo({ width, height, configs: _configs });
        _configs = componentOperator.moveTo({ x: left, y: top, configs: _configs });
        componentStates.push({
          componentId: id,
          updateData: {
            config: _configs,
          },
        });
      }
    });
    // todo: comp和panel同时更新
    componentStates.length && dispatch(updateComponentConfig(componentStates));
    panelStates.length && dispatch(updatePanel(panelStates));
  };
}

// 删除子组件
export function deleteChildComponent(componentId) {
  return async (dispatch, getState) => {
    const hide = message.loading('正在删除...', 0);
    const { createApp, componentsById } = getState();
    const { id } = createApp;
    const component = componentsById[componentId];
    const layers = [
      {
        id: componentId,
        children: [],
      },
    ];
    const result = await request(
      api.componentDelete,
      undefined,
      undefined,
      JSON.stringify({
        screenId: id,
        layers,
      }),
    );

    if (result) {
      // 过滤被删除的组件
      dispatch({
        type: actionTypes.COMPONENTS_DELETE,
        payload: { list: [componentId] },
      });

      const { parent } = component;
      const parentComponent = componentsById[parent];
      const { children = [] } = parentComponent;

      dispatch({
        type: actionTypes.COMPONENT_UPDATE,
        payload: {
          list: [
            {
              ...parentComponent,
              children: children.filter((d) => d !== componentId),
            },
          ],
        },
      });
      message.success('删除成功');
    }
    hide();
  };
}

// 复制子组件
export function copyChildComponent(id) {
  return async (dispatch, getState) => {
    const hide = message.loading('正在复制...', 0);
    const { componentsById } = getState();
    const result: any = await http.post<{
      id: number;
    }>(api.componentChildCopy, { id });

    if (result) {
      const childComponent = componentsById[id];
      // dispatch({
      //   type: actionTypes.COMPONENTS_ADD,
      //   payload: {
      //     ...childComponent,
      //     id: result.id
      //   }
      // });
      dispatch(commponentsAdd({ ...childComponent, id: result.id }));

      const { parent } = childComponent;
      const parentComponent = componentsById[parent];
      const { children = [] } = parentComponent;

      dispatch({
        type: actionTypes.COMPONENT_UPDATE,
        payload: {
          ...parentComponent,
          children: children.concat(result.id),
        },
      });
      message.success('复制成功');
    }
    hide();
  };
}

// 更新子组件
export function updateChildComponent({ id, name, show }) {
  return (dispatch, getState) => {
    const _name = name?.trim();
    if (_name) {
      dispatch(
        updateComponentConfig({
          componentId: id,
          updateData: {
            name: _name,
          },
        }),
      );
    }

    if (show !== undefined) {
      const { componentsById } = getState();
      const child = componentsById[id];
      const { config } = child;
      const newConfig = updateArrayConfig(config, { path: [0], value: show, field: 'show' });

      dispatch(
        updateComponentConfig({
          componentId: id,
          updateData: {
            config: newConfig,
          },
        }),
      );
    }
  };
}

// 刷新本地动态数据
function refreshDynamicData({ componentId, dataType, data }) {
  return (dispatch, getState) => {
    const { componentsById } = getState();
    const current = componentsById[componentId];

    if (dataType !== 'static' && dataType === current.dataType && data && data.length) {
      const newState = deepUpdate({ dataConfigs: { [dataType]: { data } } }, current);
      dispatch({
        type: actionTypes.COMPONENT_DATA_UPDATE,
        payload: { list: [newState] },
      });
    }
  };
}

// 添加拖拽上传的组件
export function addComponentByDrop(files, signal) {
  return async (dispatch) => {
    // TODO: 后面可以支持多个文件，不同类型的组件拖拽上传
    for (let i = 0; i < files.length; i++) {
      const file = files[i];
      if (file) {
        if (file.type.startsWith('image/')) {
          await dispatch(addImageComponent(file, signal));
        } else if (file.type.startsWith('video/')) {
          await dispatch(addVideoComponent(file, signal));
        }
      }
    }
  };
}

// 图片组件上传(拖拽或根据url)
export function addImageComponent(file: File | { name: string; url: string }, signal) {
  return async (dispatch, getState) => {
    if (!file) {
      return;
    }

    if (file instanceof File) {
      if (file.size > 5 * 1024 * 1024) {
        message.error('图片必须小于5M');
        return;
      }
    }

    const { constants, user, createApp, mobileConstants } = getState();
    const { userInfo } = user;
    const { tenantId } = userInfo;
    const { id: screenId, mobileId } = createApp;
    const currentConstants = mobileId ? mobileConstants.list : constants.list;
    const imageConstant = currentConstants.find((d) => d.moduleName === 'imageV2');
    if (!imageConstant) {
      message.error('图片组件不存在');
      return;
    }

    // const hide = message.loading('正在导入图片...', 0);
    const { version, moduleName } = imageConstant;
    try {
      let path: string;
      if (file instanceof File) {
        path = await uploadAssets({
          file,
          path: `data/img/${tenantId}/${screenId}/${file.name}`,
          signal,
        });
      } else {
        path = file.url;
      }

      const rect = await getImageRect(getAssetsUrl(path));

      const addedResult = await dispatch(
        addComponent({
          moduleName,
          name: file.name.split('.')[0],
          version,
          updates: [
            { path: ['chart', 'dimension', 'chartDimension', 'width'], value: rect.width },
            { path: ['chart', 'dimension', 'chartDimension', 'height'], value: rect.height },
            { path: ['chart', 'url'], value: path },
          ],
        }),
      );

      // hide();
      return addedResult;
    } catch (error) {
      // hide();
      message.error('上传失败');
    }
  };
}

// 视频组件上传(拖拽或根据url)
export function addVideoComponent(file: File | { name: string; url: string }, signal) {
  return async (dispatch, getState) => {
    if (!file) {
      return;
    }

    if (file instanceof File) {
      if (file.size > 150 * 1024 * 1024) {
        message.warning('视频不能超过150M');
        return;
      }
    }

    const { constants, user, createApp, mobileConstants } = getState();
    const { userInfo } = user;
    const { tenantId } = userInfo;
    const { id: screenId, mobileId } = createApp;

    const currentConstants = mobileId ? mobileConstants.list : constants.list;
    const videoConstant = currentConstants.find((d) => d.moduleName === 'videoV2');
    if (!videoConstant) {
      message.error('视频组件不存在');
      return;
    }

    // const hide = message.loading('正在导入视频...', 0);
    const { version, name, moduleName } = videoConstant;
    try {
      let path: string;
      if (file instanceof File) {
        path = await uploadAssets({
          file,
          path: `data/video/${tenantId}/${screenId}/${file.name}`,
          signal,
        });
      } else {
        path = file.url;
      }

      const rect = await getVideoRect(getAssetsUrl(path));

      const addedResult = await dispatch(
        addComponent({
          name,
          moduleName,
          version,
          updates: [
            { path: ['chart', 'dimension', 'chartDimension', 'width'], value: rect.width },
            { path: ['chart', 'dimension', 'chartDimension', 'height'], value: rect.height },
            { path: ['chart', 'videoSrc'], value: path },
          ],
        }),
      );

      // hide();
      return addedResult;
    } catch (error) {
      // hide();
      message.error('上传失败');
    }
  };
}

// 修改组件的数据配置
export function updateComponentDataConfig({
  id,
  useFilter,
  autoUpdate,
  data,
  fields,
}: {
  id: number;
  useFilter?: boolean;
  autoUpdate?: any;
  data?: any;
  fields?: any;
}) {
  return async (dispatch, getState) => {
    const { componentsById } = getState();
    const component = componentsById[id];
    if (!component) return;

    const { dataType, dataConfigs } = component;
    let _data = null;
    if (data && Object.keys(data).includes('sql')) {
      _data = {
        ...data,
        sql: Base64.encode(data.sql),
      };
    } else {
      _data = data;
    }

    const result: any = await http.post<{
      baseURL: string;
    }>(api.componentDataConfigUpdate, {
      id,
      type: dataType,
      useFilter,
      autoUpdate,
      data: _data,
      fields,
    });

    if (result) {
      const param: any = {
        id,
      };
      if (useFilter != undefined) {
        param.useFilter = useFilter;
      }
      if (autoUpdate != undefined) {
        param.autoUpdate = autoUpdate;
      }
      if (data != undefined || fields != undefined) {
        let config = dataConfigs[dataType] || {};
        if (dataType === 'static') {
          if (data) {
            config = { ...config, data };
          }
          if (fields) {
            config = { ...config, fields };
          }
        } else {
          if (data) {
            config = { ...config, config: data };
          }
          if (fields) {
            config = { ...config, fields };
          }
          if (dataType == 'api') {
            param.baseURL = result.baseURL;
          }
        }

        param.dataConfigs = {
          ...dataConfigs,
          [dataType]: config,
        };
      }

      dispatch({
        type: actionTypes.COMPONENT_DATA_CONFIG_UPDATE,
        payload: param,
      });

      if (dataType !== 'static' && data) {
        dispatch(getComponentData(id));
      }

      return result;
    }
  };
}

// 添加组件过滤器
export function addComponentFilter({ id, filterId }: { id: number; filterId: number }) {
  return async (dispatch, getState) => {
    const result = await http.post(api.componentFilterAdd, { id, filterId });
    if (result) {
      const { componentsById } = getState();
      const component = componentsById[id];
      const { filters } = component;

      const newFilters = filters.concat({ id: filterId, enable: true });

      dispatch({
        type: actionTypes.COMPONENT_DATA_CONFIG_UPDATE,
        payload: {
          id,
          filters: newFilters,
        },
      });

      return result;
    }
  };
}

// 移除组件过滤器
export function removeComponentFilter({ id, filterId }) {
  return async (dispatch, getState) => {
    const result = await http.post(api.componentFilterRemove, { id, filterId });

    if (result) {
      const { componentsById } = getState();
      const component = componentsById[id];
      const { filters } = component;
      const newFilters = filters.filter((d) => d.id !== filterId);

      dispatch({
        type: actionTypes.COMPONENT_DATA_CONFIG_UPDATE,
        payload: {
          id,
          filters: newFilters,
        },
      });

      return result;
    }
  };
}

export type TriggerComponentFilterParamsType = {
  id: number;
  filterId: number;
  enable: boolean;
};
// 切换是否使用过滤器
export function triggerComponentFilter({ id, filterId, enable }: TriggerComponentFilterParamsType) {
  return async (dispatch, getState) => {
    const result = await http.post(api.componentFilterTrigger, { id, filterId, enable });

    if (result) {
      const { componentsById } = getState();
      const component = componentsById[id];
      const { filters } = component;
      const newFilters = filters.map((d) => (d.id === filterId ? { ...d, enable: enable } : d));

      dispatch({
        type: actionTypes.COMPONENT_DATA_CONFIG_UPDATE,
        payload: {
          id,
          filters: newFilters,
        },
      });

      return result;
    }
  };
}

// 排序组件过滤器
export function sortComponentFilter({ id, oldIndex, newIndex }) {
  return async (dispatch, getState) => {
    if (oldIndex === newIndex) {
      return;
    }

    const { componentsById } = getState();
    const component = componentsById[id];
    if (!component) {
      return;
    }

    const { filters } = component;
    const source = filters[oldIndex];
    const target = filters[newIndex];
    const clonedFilters = filters.slice();

    clonedFilters.splice(oldIndex, 1);
    clonedFilters.splice(newIndex, 0, source);
    dispatch({
      type: actionTypes.COMPONENT_DATA_CONFIG_UPDATE,
      payload: {
        id,
        filters: clonedFilters,
      },
    });

    const result = await http.post(api.componentFilterSort, {
      id,
      origin: source.id,
      target: target.id,
    });

    if (!result) {
      dispatch({
        type: actionTypes.COMPONENT_DATA_CONFIG_UPDATE,
        payload: {
          id,
          filters: filters,
        },
      });
    }
  };
}

// 更新自定义事件配置
export function updateEvents(params) {
  return async (dispatch, getState) => {
    if (!Array.isArray(params)) {
      params = [params];
    }

    const { createApp, componentsById } = getState();
    const { layersSelected, componentsChildSelected } = createApp;
    const id = componentsChildSelected || layersSelected[0];
    const { events = [] } = componentsById[id];

    let newEvents = events;
    params.map((d) => {
      newEvents = updateDataByKey({ data: newEvents, paths: d.paths, key: d.key, value: d.value });
    });

    // fix: 一些脏数据过滤，出现 action: { animation: { config: { duration: 600}}} 这样的数据
    newEvents = newEvents.filter((d) => d.id);

    const screenId = getScreenId();
    if (!screenId) {
      message.error('大屏id 不存在');
      return;
    }
    const result = await http.post(api.componentUpdate, {
      screenId,
      configs: [
        {
          id,
          events: newEvents,
        },
      ],
    });

    if (result) {
      dispatch({
        type: actionTypes.COMPONENT_UPDATE,
        payload: {
          id,
          events: newEvents,
        },
      });
    }

    return result;
  };
}

// 升级组件
export function upgradeComponent(ids) {
  return async (dispatch, getState) => {
    const hide = message.loading('正在升级...', 0);
    const { componentsById, constants, mobileConstants, user, tenantConstants } = getState();
    // 这里需要将用户自定义组件和平台组件合并
    const currentConstants = isMobileScreen()
      ? mobileConstants.list
      : [...constants.list, ...tenantConstants.list];

    const newComponents: any = [];

    ids = Array.isArray(ids) ? ids : [ids];

    try {
      await Promise.all(
        ids.map(async (id) => {
          const component = componentsById[id];
          const {
            name,
            base,
            config,
            dataConfigs,
            children,
            actions = [],
            from,
            triggers,
          } = component;
          const { module_name: moduleName } = base;
          const constant = currentConstants.find((d) => d.moduleName === moduleName);
          const { version: latestVersion } = constant;
          let latestComponent =
            window.component && window.component[`${moduleName}@${latestVersion}`];
          if (!latestComponent) {
            // 加载组件js
            latestComponent = await getComponent(
              moduleName,
              latestVersion,
              from === 1 ? user.userInfo.tenantId : undefined,
            );
          }

          const latestDefault = latestComponent.getDefaultConfig();
          const latestData = latestComponent.getDefaultData();
          const {
            base: latestBase,
            configuration: latestConfig,
            isTrigger = false,
          } = latestDefault;
          const mergedConfig = mergeConfig(latestConfig, config);
          let mergedChildrenConfig = [];

          if (children && children.length) {
            const childrenConfigs = latestComponent.getDefaultChildrenConfig();
            const childrenData = latestComponent.getDefaultChildrenData();

            mergedChildrenConfig = children
              .map((d) => {
                const child = componentsById[d];
                const {
                  name: childName,
                  dataConfigs: childDataConfigs,
                  base: childBase,
                  config: childConfig,
                  actions: childActions = [],
                } = child;
                const { module_name: childModuleName } = childBase;
                const latestChildDefaultConfig = childrenConfigs.find(
                  (v) => v.base.module_name === childModuleName,
                );
                const latestChildData =
                  childrenData.find((v) => v.id === childModuleName) ||
                  childrenData.find((v) => v.id === latestChildDefaultConfig.id) ||
                  {};

                const newChildDataConfigs = Object.keys(childDataConfigs).reduce((all, item) => {
                  all[item] = {
                    ...childDataConfigs[item],
                    fields: mergeFields(latestChildData.fields, childDataConfigs[item].fields),
                  };
                  return all;
                }, {});

                if (latestChildDefaultConfig) {
                  const {
                    base: latestChildBase,
                    configuration: latestChildConfig,
                    actions: latestChildActions = [],
                    triggers: latestChildTriggers = [],
                  } = latestChildDefaultConfig;

                  const newChildActions = latestChildActions.map((item) => {
                    const oldAction = childActions.find((o) => o.value === item.value);
                    return {
                      ...item,
                      ...(item.config
                        ? oldAction && oldAction.config
                          ? { config: oldAction.config }
                          : { config: item.config }
                        : {}),
                    };
                  });

                  return {
                    componentId: d,
                    updateData: {
                      config: mergeConfig(latestChildConfig, childConfig),
                      base: latestChildBase,
                      name: childName,
                      dataConfigs: newChildDataConfigs,
                      actions: newChildActions,
                      triggers: latestChildTriggers,
                    },
                  };
                } else {
                  return null;
                }
              })
              .filter((d) => d);
          }

          const newDataConfigs = Object.keys(dataConfigs).reduce((all, item) => {
            all[item] = {
              ...dataConfigs[item],
              fields: mergeFields(latestData.fields, dataConfigs[item].fields),
            };
            return all;
          }, {});

          const newActions =
            latestDefault.actions &&
            latestDefault.actions.map((d) => {
              const oldAction = actions.find((o) => o.value === d.value);

              return {
                ...d,
                ...(d.config
                  ? oldAction && oldAction.config
                    ? { config: oldAction.config }
                    : { config: d.config }
                  : {}),
              };
            });
          if (isTrigger) {
            mergedConfig[0].isTrigger = isTrigger;
          }

          const newTriggers =
            Array.isArray(triggers) && triggers.length ? triggers : latestDefault.triggers || [];
          newComponents.push(
            {
              componentId: id,
              updateData: {
                config: mergedConfig,
                base: latestBase,
                name,
                dataConfigs: newDataConfigs,
                actions: newActions || [],
                triggers: newTriggers,
              },
            },
            ...mergedChildrenConfig,
          );
        }),
      );
    } catch (error) {
      hide();
      message.error('升级失败');
      console.error(error);
      return;
    }

    const result = await dispatch(updateComponentConfig(newComponents));
    hide();
    if (result) {
      message.success('升级成功');
    }
  };
}

// 组件绑定数据容器
export function bindContainer({ screenId, containerId, binding }) {
  return async (dispatch, getState) => {
    const { createApp } = getState();
    const rootScreenId = getTopScreenId();
    const { layersSelected, componentsChildSelected } = createApp;
    const id = componentsChildSelected || layersSelected[0];
    if (!id) {
      return;
    }
    const hide = message.loading('更新中...', 0);
    const result = await request(
      api.bindContainer,
      {
        componentId: id,
      },
      undefined,
      JSON.stringify({
        screenId,
        containerId,
        binding,
        rootScreenId: rootScreenId,
      }),
    );
    if (result) {
      dispatch({
        type: actionTypes.COMPONENT_DATA_CONTAINER_CONFIG_UPDATE,
        payload: {
          id,
          dataContainers: result,
        },
      });
      if (binding) {
        // 绑定的时候再查一次数据
        dispatch(getContainerData(containerId));
      }
    }
    hide();
    return result;
  };
}

// 抽离COMPONENTS_ADD的action
export function commponentsAdd(payloadValue) {
  return async (dispatch, getState) => {
    const { user, screenDetail } = getState();
    const authKey = user.userInfo.edition?.id || screenDetail.authKey;
    const { id } = payloadValue;
    let payload = null;

    if (id) {
      payload = payloadValue;
    } else {
      payload = { ...payloadValue, list: payloadValue.list };
    }

    dispatch({
      type: actionTypes.COMPONENTS_ADD,
      payload: payload,
    });
  };
}

const initialState: ComponentsById = {};

export default function reducer(state = initialState, { type, payload }): ComponentsById {
  switch (type) {
    case actionTypes.COMPONENTS_ADD: {
      const { list, id } = payload;
      if (id) {
        return {
          ...state,
          [id]: payload,
        };
      } else {
        return updateByKeyname('id', list, state);
      }
    }
    case actionTypes.COMPONENT_DATA_GET_SUCCESS: {
      const component = state[payload.id];
      if (!component) {
        return state;
      }

      const { dataType, dataConfigs } = component;
      return {
        ...state,
        [payload.id]: {
          ...component,
          dataConfigs: {
            ...dataConfigs,
            [dataType]: {
              ...dataConfigs[dataType],
              data: payload.data,
            },
          },
          errorMessage: null,
        },
      };
    }

    case actionTypes.COMPONENT_DATA_GET_FAILURE: {
      const component = state[payload.id];
      if (!component) {
        return state;
      }
      const { dataType, dataConfigs } = component;
      return {
        ...state,
        [payload.id]: {
          ...component,
          dataConfigs: {
            ...dataConfigs,
            [dataType]: {
              ...dataConfigs[dataType],
              data: payload.data,
            },
          },
          errorMessage: payload.message,
        },
      };
    }
    case actionTypes.THEME_COMPONENT_UPDATE: {
      const { list, id } = payload;
      if (id) {
        return {
          ...state,
          [id]: {
            ...state[id],
            ...payload,
          },
        };
      } else {
        return updateByKeyname('id', list, state);
      }
    }
    case actionTypes.COMPONENT_DATA_CONTAINER_CONFIG_UPDATE:
    case actionTypes.COMPONENT_UPDATE: {
      const { list, id } = payload;
      if (id) {
        return {
          ...state,
          [id]: {
            ...state[id],
            ...payload,
          },
        };
      } else {
        return updateByKeyname('id', list, state);
      }
    }
    case actionTypes.COMPONENT_DATA_UPDATE: {
      const { list } = payload;
      return updateByKeyname('id', list, state);
    }

    case actionTypes.HYBRID_UPDATE: {
      const { components } = payload;
      if (!components.length) return state;
      return updateByKeyname('id', components, state);
    }

    case actionTypes.COMPONENT_SOURCE_CHANGE: {
      const { id, type } = payload;
      return {
        ...state,
        [id]: {
          ...state[id],
          dataType: type,
        },
      };
    }

    case actionTypes.COMPONENTS_DELETE: {
      const { list } = payload;
      return deleteByKey(list, state);
    }

    case actionTypes.COMPONENT_DATA_CONFIG_UPDATE: {
      return {
        ...state,
        [payload.id]: {
          ...state[payload.id],
          ...payload,
        },
      };
    }

    case actionTypes.CLEAR: {
      return {};
    }

    default:
      return state;
  }
}
