import omitBy from 'lodash/omitBy';
import isUndefined from 'lodash/isUndefined';
import http from 'utils/fetch';
import { Utils } from '@easyv/react-gui';
import { api } from 'constants/config';
import { actionTypes } from 'constants/action-types';
import { screenOperator, isScreen, isMobileScreen } from 'utils/screen';
import {
  sort,
  toFront,
  toBack,
  toUp,
  toDown,
  groupBy,
  cancelGroup,
  getPanelsFromLayers,
  toObject,
} from 'utils/layer';
import { deepUpdate, arrayExpand } from 'utils/data-helper';
import { transformComponents } from 'utils/component';
import { updateScreen } from 'models/screens';
import { getPanelInfoForScreenEdit } from './panel';
import { isComponent, isGeneralPanel, isGroup, isEditPage, parseSearchParams } from 'utils/utils';
import { getId } from 'utils/interaction';
import { ScreensById } from './screen.types';
import { setScreenResult } from 'models/screen-detail';
import { getFilters } from './filter';
import { getSharedPortalId } from 'utils/spaceship';
import { getDataContainers } from 'models/data-container';
import { commponentsAdd } from './component';
import isObject from 'lodash/isObject';
import defaultScreenConfig from 'constants/default-screen-config';
import { ScreenType } from '@/models/screen-detail';
import { getScreenDataByPathName } from '@/services/screen';
import { dispatchUpdateScreenStatus } from './screensStatus';
const { mergeConfig } = Utils;

const isPreview = () => /\/(shareScreen|insert)/i.test(window.location.pathname);

function screenMergeConfig(screen) {
  if (!screen.panelId && !screen.mobileId) {
    // root screen
    return {
      ...screen,
      config: mergeConfig(defaultScreenConfig.config, screen.config),
    };
  }
  return screen;
}

export function getScreen(id, signal?, params: any = {}) {
  return async (dispatch, getState) => {
    if (!id) {
      return;
    }
    const {
      screensById,
      filters,
      screenDetail,
      screenEdit,
      user: { userInfo },
    } = getState();
    const { screenState } = screenEdit;
    const portalId = getSharedPortalId();

    const screen = screensById[id];
    if (!screen) {
      let result,
        param: any = { id };
      const token = parseSearchParams()?._easyv_token;
      if (token) {
        param = {
          ...param,
          token: token,
        };
      }
      if (portalId) {
        param = {
          ...param,
          portalId,
        };
      }
      if (params.byControlCode) {
        param = {
          ...param,
          controlCode: params.byControlCode,
        };
        const { id, ...rest } = param;
        result = await getScreenDataByPathName({
          params: {
            screenId: id,
            portalId,
          },
          query: rest,
          options: { signal },
          userInfo,
        });
      } else {
        const { id, ...rest } = param;
        result = await getScreenDataByPathName({
          params: {
            screenId: id,
            portalId,
          },
          query: rest,
          options: {
            signal,
            error: (err) => {
              if (/租户已过期/.test(err.message) && !isPreview) {
                // 编辑页才会调用
                window.location.href = window.appConfig.HOME_URL + '/overdue';
              }
            },
          },
          userInfo,
        });
      }
      if (screenDetail.id === param.id) {
        dispatch(setScreenResult(result));
      }
      dispatchUpdateScreenStatus(dispatch, {
        screenId: id,
        statusCode: result,
      });
      // result 为-1 表示大屏需要分享密码 -2表示token
      if (result && typeof result === 'object') {
        const state = screenOperator.createFromServer(result);

        dispatch({
          type: actionTypes.SCREEN_GET,
          payload: screenMergeConfig(state),
        });

        const components = transformComponents(result.components, id);

        dispatch(commponentsAdd({ list: components }));

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

        dispatch({
          type: actionTypes.CALLBACKS_INIT,
          payload: {
            components,
            filters: filters.list,
          },
        });

        dispatch({
          type: actionTypes.EVENTS_INIT,
          payload: {
            components,
            layers: result.layers,
          },
        });

        const { layers } = result;
        let panels = getPanelsFromLayers(layers);
        if (isEditPage() && !screenState) {
          panels = panels.filter((d) => d.show && !d.hideDefault);
        }
        await Promise.all(
          panels.map((d) => dispatch(getPanelInfoForScreenEdit(getId(d.id), signal))),
        );
      }
      return result;
    } else {
      if (screenState === 1) {
        // 二轮数据
        const { layers } = screen;
        const panels = getPanelsFromLayers(layers);
        await Promise.all(panels.map((d) => dispatch(getPanelInfoForScreenEdit(getId(d.id)))));
      }
      return screen;
    }
  };
}

export function getScreenInfo(id: number, signal?: AbortSignal, params: any = {}) {
  return async (dispatch, getState) => {
    const {
      screensById,
      filters,
      screenDetail,
      user: { userInfo },
    } = getState();
    let { list: filterList } = filters;
    const screen = screensById[id];

    if (!screen) {
      let result,
        param: any = { id };
      const params = parseSearchParams();
      const token = params?._easyv_token;
      if (token) {
        param = {
          ...param,
          token: token,
        };
      }

      if (params.byControlCode) {
        param = {
          ...param,
          controlCode: params.byControlCode,
        };
        const { id, ...rest } = param;
        result = await getScreenDataByPathName({
          params: { screenId: id },
          query: rest,
          options: { signal },
          userInfo,
        });
      } else {
        const { id, ...rest } = param;
        let _state;
        result = await getScreenDataByPathName({
          params: { screenId: id },
          query: rest,
          options: {
            signal,
            error: (err) => {
              if (/租户已过期/.test(err.message) && isPreview) {
                _state = ScreenType.USER_EXPIRED;
              }
            },
          },
          userInfo,
        });
        if (_state === ScreenType.USER_EXPIRED) {
          dispatch({
            type: actionTypes.SCREEN_DETAIL_GET_RESULT,
            payload: ScreenType.USER_EXPIRED,
          });
          return ScreenType.USER_EXPIRED;
        }
      }
      if (screenDetail.id === param.id) {
        dispatch(setScreenResult(result));
      }
      dispatchUpdateScreenStatus(dispatch, {
        screenId: id,
        statusCode: result,
      });
      // result 为-1 表示大屏需要分享密码 -2表示token -3表示大屏所属用户账户已过期
      if (result && typeof result === 'object') {
        if (result.panelId == null) {
          filterList = await dispatch(getFilters(id));
          await dispatch(getDataContainers(id));
        }

        const state = screenOperator.createFromServer(result);

        dispatch({
          type: actionTypes.SCREEN_GET,
          payload: screenMergeConfig(state),
        });

        const components = transformComponents(result.components, id);
        dispatch(commponentsAdd({ list: components }));

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

        dispatch({
          type: actionTypes.CALLBACKS_INIT,
          payload: {
            components,
            filters: filterList,
          },
        });

        dispatch({
          type: actionTypes.EVENTS_INIT,
          payload: {
            components,
            layers: result.layers,
          },
        });
      }
      return result;
    } else {
      return screen;
    }
  };
}

/**
 *
 *
 * @export
 * @param {*} values [{id, key, value}], id为图层id，key为图层的某个字段（collapsed/show/lock/hideDefault）,value表示是对应的值
 * @returns
 */

export function updateLayersKey({ values }) {
  return async (dispatch, getState) => {
    const { createApp } = getState();
    const { id } = createApp;

    const newState = values;

    const data = await http.post(api.screenLayersChange, {
      id,
      configs: newState,
    });

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

export function updateLayerName({ id, value }) {
  return (dispatch) => {
    const update = [{ id, key: 'name', value }];
    dispatch(updateLayersKey({ values: update }));

    if (isComponent(id)) {
      dispatch({
        type: actionTypes.COMPONENT_UPDATE,
        payload: {
          list: [{ id, name: value }],
        },
      });
    } else if (isGeneralPanel(id)) {
      dispatch({
        type: actionTypes.PANEL_UPDATE,
        payload: {
          list: [{ id: getId(id), name: value }],
        },
      });
    }
  };
}

// 更新图层状态
// 接受panelid componentid groupid
export function changeLayerStatus(optionKey, layerId, value) {
  return (dispatch) => {
    if ('focus' === optionKey) return dispatch(changeFocusStatus(layerId, value));
    if (!layerId) {
      return dispatch(changeSelectedLayerStatus(optionKey));
    }
    const layerIds = Array.isArray(layerId) ? layerId : [layerId];
    dispatch(updateLayersKey({ values: layerIds.map((id) => ({ id, key: optionKey, value })) }));
  };
}

// 修改选中图层的状态
function changeSelectedLayerStatus(optionKey) {
  return (dispatch, getState) => {
    const { createApp, screensById } = getState();
    const { id: screenId, layersSelected } = createApp;
    const layers = screensById[screenId].layers;

    const items: any = [];
    arrayExpand(
      layers,
      (item) => item.components,
      (item) => {
        if (~layersSelected.indexOf(item.id)) items.push(item);
      },
    );
    const value = !items.every((item) => item[optionKey]);
    dispatch(
      updateLayersKey({ values: layersSelected.map((id) => ({ id, key: optionKey, value })) }),
    );
  };
}

// 修改图层的focus状态
function changeFocusStatus(layers, status) {
  return (dispatch, getState) => {
    const { layersSelected, focusLayers } = getState().createApp;
    if (!layers) {
      layers = layersSelected;
      status = !layers.every((id) => ~focusLayers.indexOf(id));
    }
    if (status) {
      dispatch({
        type: actionTypes.FOCUS_LAYER_ADD,
        payload: layers,
      });
    } else {
      dispatch({
        type: actionTypes.FOCUS_LAYER_DELETE,
        payload: layers,
      });
    }
  };
}

export function updateScreenStyle({ id, config }) {
  // 修改大屏样式
  return async (dispatch) => {
    dispatch({
      type: actionTypes.SCREEN_UPDATE_SUCCESS,
      payload: {
        id,
        config,
      },
    });
  };
}

export function resetScreenConfig() {
  // 回滚大屏config
  return async (dispatch, getState) => {
    const state = getState();
    const { screensById, themes } = state;
    const {
      themeScreenConfig,
    }: {
      themeScreenConfig: {
        screenId: string;
        config: any[];
      };
    } = themes;
    if (isObject(themeScreenConfig) && Object.keys(themeScreenConfig).length) {
      const { screenId, config } = themeScreenConfig;
      const screen = screensById[screenId];
      if (screen) {
        // 主题应用回滚
        await dispatch({
          type: actionTypes.SCREEN_CONFIG_UPDATE,
          payload: {
            id: screen.id,
            config,
          },
        });
      }
    }
  };
}

export function updateLayers({ id, layers }) {
  return async (dispatch) => {
    dispatch({
      type: actionTypes.LAYER_UPDATE,
      payload: {
        id,
        layers,
      },
    });

    const result = await http.post(api.screenLayersUpdate, {
      id,
      layers,
    });

    if (result) {
      dispatch({
        type: actionTypes.LAYER_UPDATE,
        payload: {
          id,
          layers: result,
        },
      });
      return result;
    }
  };
}

export function groupByLayers() {
  return (dispatch, getState) => {
    const { createApp, screensById } = getState();
    const { id, layersSelected } = createApp;
    if (!layersSelected.length) {
      return;
    }
    const screen = screensById[id];
    const { layers } = screen;
    const [newLayers, newGroupId] = groupBy(layers, layersSelected);
    dispatch(
      updateLayers({
        id,
        layers: newLayers,
      }),
    );

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

export function cancelGroupLayers() {
  return (dispatch, getState) => {
    const { createApp, screensById } = getState();
    const { id, layersSelected } = createApp;
    if (!layersSelected.length) {
      return;
    }
    const groupSelected = layersSelected.filter((d) => isGroup(d));
    if (!groupSelected.length) {
      return;
    }

    const screen = screensById[id];
    const { layers } = screen;

    const [newLayers, newSelected] = cancelGroup(layers, groupSelected);
    dispatch({
      type: actionTypes.LAYER_SELECT,
      payload: {
        selected: newSelected,
      },
    });

    dispatch(
      updateLayers({
        id,
        layers: newLayers,
      }),
    );
  };
}

export function sortLayers({ from, to, oldIndex, newIndex, item }) {
  return (dispatch, getState) => {
    const { createApp, screensById } = getState();
    // eslint-disable-next-line prefer-const
    let { id, layersSelected } = createApp;
    const screen = screensById[id];
    const { layers } = screen;

    let {
      dataset: { id: fromId },
    } = item;
    fromId = isNaN(+fromId) ? fromId : +fromId;
    // 如果拖动的是未选中的图层，则选中它
    if (layersSelected.indexOf(fromId) === -1) {
      layersSelected = [fromId];
    }
    const fromGroupId = from.id.slice(2);
    const toGroupId = to.id.slice(2);
    const layersObj = toObject(layers);
    const toGroupIdPath = (layersObj[toGroupId] && layersObj[toGroupId].groupPath) || [];

    const disableSort = layersSelected.some((d) => {
      return isGroup(d) && (toGroupId === d || toGroupIdPath.indexOf(d) > -1);
    });

    if (disableSort) {
      return;
    }

    const result = sort({
      layers,
      fromGroupId: fromGroupId,
      fromIndex: oldIndex,
      toGroupId: toGroupId,
      toIndex: newIndex,
      selected: layersSelected,
    });

    dispatch(
      updateLayers({
        id,
        layers: result,
      }),
    );
  };
}

export function sortLayersDirection(direction) {
  return (dispatch, getState) => {
    const { createApp, screensById } = getState();
    const { id, layersSelected } = createApp;
    if (!layersSelected.length) {
      return;
    }
    const screen = screensById[id];
    const { layers } = screen;

    const directionFunc = {
      top: toFront,
      bottom: toBack,
      up: toUp,
      down: toDown,
    };
    const newLayers = directionFunc[direction](layers, layersSelected);

    dispatch(
      updateLayers({
        id,
        layers: newLayers,
      }),
    );
  };
}

// 修改背景图配置
export function updateScreenConfig(newConfig) {
  return (dispatch, getState) => {
    const { createApp } = getState();
    const { id: screenId } = createApp;

    dispatch(
      updateScreen({
        id: screenId,
        config: newConfig,
      }),
    );
  };
}

// 大屏的的滤镜配置
export function updateScreenFilter({ config }) {
  return async (dispatch, getState) => {
    const { createApp } = getState();
    const { id } = createApp;

    dispatch({
      type: actionTypes.SCREEN_UPDATE_SUCCESS,
      payload: {
        id: id,
        globalFilter: config,
      },
    });

    const result = await http.post(api.screenUpdate, { id, globalFilter: config });

    return result;
  };
}

export function updateThumb(thumb) {
  return async (dispatch, getState) => {
    const { createApp } = getState();
    const { id } = createApp;
    const result = await http.post(api.screenUpdate, {
      id,
      thumb,
    });

    if (result) {
      dispatch({
        type: actionTypes.UPDATE_THUMB,
        payload: {
          id,
          thumb,
        },
      });
    }
  };
}

export function updateTopScreen({ id, dataAlert, showLoading }) {
  return async (dispatch) => {
    const result = await http.post(api.screenUpdate, {
      id,
      isShowAlert: dataAlert,
      isShowLoading: showLoading,
    });

    if (result) {
      const updated = omitBy(
        {
          id,
          dataAlert,
          showLoading,
        },
        isUndefined,
      );
      dispatch({
        type: actionTypes.SCREEN_UPDATE_SUCCESS,
        payload: updated,
      });
    }
  };
}

export function updateHideDefault(value) {
  return (dispatch, getState) => {
    const { createApp } = getState();
    const { layersSelected } = createApp;

    const newState = layersSelected.map((d) => ({
      id: d,
      key: 'hideDefault',
      value: value,
    }));

    dispatch(updateLayersKey({ values: newState }));
  };
}

const initialState: ScreensById = {};

export default function reducer(state = initialState, { type, payload }): ScreensById {
  switch (type) {
    case actionTypes.SCREEN_GET: {
      // TODO: 后续改掉，暂且处理
      if (!isScreen && !isMobileScreen && state[payload.id]) {
        return state;
      }
      return {
        ...state,
        [payload.id]: payload,
      };
    }

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

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

    case actionTypes.SCREEN_CONFIG_UPDATE: {
      const { id, config } = payload;
      if (state[id]) {
        return {
          ...state,
          [id]: {
            ...state[id],
            config: config,
          },
        };
      } else {
        return state;
      }
    }
    case actionTypes.SCREEN_SHARE_UPDATE: {
      const { id, share, sharePassword, shareToken, shareInfo } = payload;
      if (state[id]) {
        return {
          ...state,
          [id]: {
            ...state[id],
            share,
            sharePassword,
            shareToken,
            shareInfo,
          },
        };
      } else {
        return state;
      }
    }

    case actionTypes.LAYER_UPDATE: {
      const { id, layers } = payload;
      return deepUpdate({ [id]: { layers } }, state);
    }

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

    case actionTypes.CLEAR: {
      return {};
    }

    default:
      return state;
  }
}
