import { actionTypes } from 'constants/action-types';
import { api } from 'constants/config';
import http, { request } from 'utils/fetch';
import { getTopScreenId } from 'utils/screen';
import { updateDataByKey, jsonToFormdata, urlCacheReplace } from 'utils/utils';
import { Base64 } from 'js-base64';
import {
  getCurComponentCallbackValues,
  getReplacedCallbackApiQuery,
  getReplacedCallbackSqlQuery,
} from 'utils/component';
import { JsQueue } from 'utils/queue';
import message from 'components/message';
import qs from 'qs';
import { isCancelRequestData } from 'utils/convert';
import { DataContainersType } from './data-container.types';
import { dtableapi } from 'constants/default-dtable';
import { dispatchDebugScreenEvent, getDebugConfigFromSessionStorage } from '@/utils/debug-screen';

const jsQueue = new JsQueue();
jsQueue.start();

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

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

const transfromDataConfigs = ({ dataConfig, ...rest }) => {
  const param = {};
  for (const key in dataConfig) {
    param[key] = {
      config: dataConfig[key].data,
    };
  }
  if (rest.dataType === 'dtableapi' && !dataConfig['dtableapi']) {
    param['dtableapi'] = dtableapi;
  }
  return {
    ...rest,
    dataConfigs: {
      ...param,
      static: rest.staticData,
    },
  };
};

// 删除
export function deleteContainer(id, components) {
  return async (dispatch, getState) => {
    const result = await request(api.deleteContainer, {
      id,
    });
    if (result) {
      dispatch({
        type: actionTypes.DATA_CONTAINER_DELETE,
        payload: id,
      });

      const { componentsById } = getState();

      const newComponents = components.map((componentId) => {
        const component = componentsById[componentId];
        const { dataContainers } = component;
        const newDataContainers = dataContainers.filter((o) => o.id !== id);
        return {
          ...component,
          dataContainers: newDataContainers,
        };
      });
      dispatch({
        type: actionTypes.COMPONENT_DATA_UPDATE,
        payload: {
          list: newComponents,
        },
      });
    }

    return result;
  };
}

export function getDataContainers(screenId) {
  return async (dispatch, getState) => {
    if (!screenId) {
      return;
    }
    const { filters } = getState();
    let result = await request(urlCacheReplace(api.screenDataList), {
      screenId,
    });
    if (result && Array.isArray(result)) {
      result = result.map((d) => {
        return {
          screenId,
          ...transfromDataConfigs(d),
        };
      });
      await dispatch({
        type: actionTypes.DATA_CONTAINER_LIST,
        payload: result,
      });
      dispatch({
        type: actionTypes.CALLBACKS_DATA_CONTAINER_INIT,
        payload: {
          dataContainers: result,
          filters: filters.list,
        },
      });
      result.map((d) => {
        if (d.dataType !== 'static') {
          dispatch(getContainerData(d.id));
        }
      });
    }

    return result;
  };
}

// 更新
export function updateDataContainer(id, params = {}) {
  return async (dispatch) => {
    let result = await request(
      api.editScreenData,
      {
        id,
      },
      undefined,
      JSON.stringify({
        ...params,
      }),
    );
    if (result) {
      result = transfromDataConfigs(result);
      dispatch({
        type: actionTypes.DATA_CONTAINER_UPDATE,
        payload: result,
      });
    }
    return result;
  };
}

// 新增
export function addDataContainer() {
  return async (dispatch, getState) => {
    const { user } = getState();
    const screenId = getTopScreenId();
    const param = {
      screenId,
    };
    if (user.isPerson) {
      param['dataType'] = 'dtableapi';
    }
    let result = await request(api.addScreenData, undefined, undefined, JSON.stringify(param));

    if (result) {
      result = transfromDataConfigs(result);
      dispatch({
        type: actionTypes.DATA_CONTAINER_ADD,
        payload: {
          ...result,
          ...param,
        },
      });
      if (result.type !== 'static') {
        dispatch(getContainerData(result.id));
      }
    }

    return result;
  };
}

export function copyScreenData(id) {
  return async (dispatch) => {
    const screenId = getTopScreenId();

    let result = await request(api.copyScreenData, {
      id,
    });
    if (result) {
      result = transfromDataConfigs(result);
      dispatch({
        type: actionTypes.DATA_CONTAINER_ADD,
        payload: {
          ...result,
          screenId,
        },
      });
      if (result.type !== 'static') {
        dispatch(getContainerData(result.id));
      }
    }

    return result;
  };
}

// 数据容器更新
export function dataUpdateEvents(params) {
  return async (dispatch, getState) => {
    if (!Array.isArray(params)) {
      params = [params];
    }
    const { dataContainers } = getState();
    const { current: id, list } = dataContainers;
    const current = list.find((d) => d.id === id);
    if (!current) {
      message.error('容器不存在');
      return;
    }

    const { events = [] } = current;

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

    const result = await request(
      api.editScreenData,
      {
        id,
      },
      undefined,
      JSON.stringify({
        events: newEvents,
      }),
    );

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

    return result;
  };
}

export function updateContainerFilter({ id, type, containerId, ...rest }) {
  return async (dispatch) => {
    if (containerId && id) {
      let url = '';
      switch (type) {
        case 'delete':
          url = api.deleteContainerFilter;
          break;
        case 'bind':
          url = api.bindContainerFilter;
          break;
        case 'useFilter':
          url = api.editScreenData;
          break;
        case 'enable':
        case 'sort':
        default:
          url = api.editContainerFilter;
      }
      if (!url) {
        return;
      }
      const screenId = getTopScreenId();

      let result = await request(
        url,
        { id: containerId },
        undefined,
        JSON.stringify({
          ...rest,
          rootScreenId: screenId,
        }),
      );

      if (result) {
        result = {
          ...transfromDataConfigs(result),
          screenId,
        };
        await dispatch({
          type: actionTypes.DATA_CONTAINER_CONFIG_UPDATE,
          payload: Object.assign(result, { id: containerId }),
        });
        if (result.dataType !== 'static' && type == 'bind') {
          dispatch(getContainerData(containerId));
        }
      }
      return result;
    }
  };
}

export function addContainerFilter({ id, filterId }: { id: number; filterId: number }) {
  return async (dispatch, getState) => {
    const { dataContainers } = getState();
    const { list } = dataContainers;
    const current = list.find((d) => d.id === id);
    if (!current) {
      return;
    }

    const isFind = current.filters.some((d) => d.id === filterId);

    const _filters = isFind
      ? current.filters.map((d) => {
          if (d.id === filterId) {
            return {
              ...d,
              enable: true,
            };
          }
          return d;
        })
      : [
          ...current.filters,
          {
            id: filterId,
            enable: true,
          },
        ];

    const param = {
      filters: _filters,
    };
    const result: any = await request(api.editScreenData, { id }, undefined, JSON.stringify(param));
    if (result) {
      dispatch({
        type: actionTypes.DATA_CONTAINER_CONFIG_UPDATE,
        payload: Object.assign(param, { id }),
      });
    }

    return result;
  };
}

// 修改组件的数据配置
export function updateComponentDataConfig({
  useFilter,
  autoUpdate,
  dataContainerId,
  data,
  fields,
}: {
  useFilter?: boolean;
  autoUpdate?: any;
  data?: any;
  dataContainerId: number;
  fields?: any;
}) {
  return async (dispatch, getState) => {
    const { dataContainers } = getState();
    const { list } = dataContainers;
    const current = list.find((d) => d.id === dataContainerId);
    if (!current) {
      return;
    }
    const { dataType, dataConfigs } = current;
    let _data = null;
    if (data && Object.keys(data).includes('sql')) {
      _data = {
        ...data,
        sql: Base64.encode(data.sql),
      };
    } else {
      _data = data;
    }

    const result: any = await request(
      api.containerSource,
      { id: dataContainerId },
      undefined,
      JSON.stringify({
        type: dataType,
        useFilter,
        autoUpdate,
        data: _data,
        fields,
      }),
    );

    if (result) {
      const param: any = {
        id: dataContainerId,
      };
      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.DATA_CONTAINER_CONFIG_UPDATE,
        payload: param,
      });

      if (dataType !== 'static' && data) {
        dispatch(getContainerData(dataContainerId));
      }

      return result;
    }
  };
}

// 修改组件数据源类型
export function changeDataSourceType({ type, containerId }) {
  return async (dispatch, getState) => {
    const { dataContainers } = getState();
    const { list } = dataContainers;
    const current = list.find((d) => d.id === containerId);
    if (!current) {
      return;
    }
    // 如果有数据请求，则取消
    signalStore[containerId] && signalStore[containerId].abort();
    const result: any = await request(
      api.containerSource,
      {
        id: containerId,
      },
      undefined,
      JSON.stringify({
        type,
      }),
    );

    if (result) {
      const { dataConfigs } = current;
      const param: any = {
        id: containerId,
        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.DATA_CONTAINER_CONFIG_UPDATE,
        payload: param,
      });

      // 切换数据源，如果不是静态数据的话，需要请求数据
      if (type !== 'static') {
        dispatch(getContainerData(containerId));
      }
    }

    return result;
  };
}

// controller 用于中断请求 一个是来源于组件  一个自身请求创建
export function getContainerData(id, controller?) {
  return (dispatch, getState) => {
    if (isCancelRequestData()) {
      return;
    }
    const { callback, dataContainers } = getState();
    const { list } = dataContainers;
    const dataContainer = list.find((d) => d.id === id);
    if (dataContainer) {
      const { dataType, dataConfigs, baseURL, name } = dataContainer;

      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)) {
          const { callbackValues } = callback;
          updateParam = {
            ...updateParam,
            ...getReplacedCallbackApiQuery({ callbackValues, config }),
          };
          const { params, path, headers, body } = updateParam;
          const reg = /:([a-zA-Z]\w*)|(\$\{([\w\W]+?)\}\$)/;
          // 如果替换完之后还存在:开头的说明被替换的存在undefined
          if (reg.test(params) || reg.test(path) || reg.test(headers) || reg.test(body)) {
            return;
          }
          if (!config.reqFromBack) {
            if (baseURL) {
              dispatch(
                getComponentDataFromFront({
                  id,
                  url: baseURL + path,
                  method: config.method,
                  needCookie: config.needCookie,
                  controller,
                  headers,
                  body,
                  params,
                  name,
                }),
              );
            }
            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(getComponentDataFromBack({ ...updateParam, signal, name }));
      }
    }
  };
}

function getComponentDataFromBack(updateParam) {
  const { dataRequest, debugListenStatus } = getDebugConfigFromSessionStorage();
  return async (dispatch, getState) => {
    const {
      id: containerId,
      params,
      body,
      path,
      headers,
      callbackValues,
      signal,
      name,
    } = updateParam;
    const { dataContainers, callback } = getState();
    const usedCallbackValues = getCurComponentCallbackValues(containerId, callback, 'containers');
    const { list } = dataContainers;
    const dataContainer = list.find((d) => d.id === containerId);

    const _key = dataContainer && getKey(updateParam);
    const url = urlCacheReplace(api.containerDataGet);

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

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

    const data = await jsQueue.http({
      http: http.post,
      body: [
        `${url}?containerId=${containerId}${_originQuery}`,
        { containerId, query: params, path, body, callbackValues, headers },
        {
          signal: signal,
          headers: _headers,
          error(response) {
            dispatchDebugScreenEvent<'error', 'dataRequest'>({
              type: 'error',
              id: containerId,
              dispatchFrom: 'dataRequest',
              name,
              debugMsg: {
                message: response.message,
                callbackValues:
                  getCurComponentCallbackValues(containerId, callback, 'containers') || {},
                data: '暂未获取到数据',
              },
            });
            dispatch({
              type: actionTypes.DATA_COMPONENT_GET_FAILURE,
              payload: {
                id: containerId,
                data: response.data,
                message: response.message,
              },
            });
          },
        },
      ],
    });
    if (componentDataList[_key]) {
      componentDataList[_key] = null;
    }
    if (data) {
      dispatch({
        type: actionTypes.DATA_COMPONENT_GET_SUCCESS,
        payload: {
          id: containerId,
          data,
        },
      });
      dataRequest &&
        debugListenStatus === 'recording' &&
        dispatchDebugScreenEvent<'info', 'dataRequest'>({
          id: containerId,
          dispatchFrom: 'dataRequest',
          name,
          type: 'info',
          debugMsg: {
            data,
            callbackValues: usedCallbackValues || {},
          },
        });
    }
  };
}

function getComponentDataFromFront(updateParams) {
  const { dataRequest, debugListenStatus } = getDebugConfigFromSessionStorage();
  return async (dispatch, getState) => {
    let { url, headers, body } = updateParams;
    const {
      method = 'get',
      id: containerId,
      params = '',
      needCookie,
      controller,
      name,
    } = updateParams;
    const _key = getKey(updateParams);
    const { callback } = getState();

    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[containerId] && signalStore[containerId].abort(); // 取消请求
      signalStore[containerId] =
        controller?.signal?.aborted === false
          ? controller
          : window.AbortController && new AbortController();
      const signal = signalStore[containerId] && signalStore[containerId].signal;
      componentDataList[_key] = containerId;
      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);
          error.response = response;
          throw error;
        }
      });
      // 成功的时候会执行
      if (componentDataList[_key]) {
        componentDataList[_key] = null;
      }

      const data = Array.isArray(result) ? result : [result];
      dispatch({
        type: actionTypes.DATA_COMPONENT_GET_SUCCESS,
        payload: {
          id: containerId,
          data: data,
        },
      });
      dataRequest &&
        debugListenStatus === 'recording' &&
        dispatchDebugScreenEvent<'info', 'dataRequest'>({
          id: containerId,
          dispatchFrom: 'dataRequest',
          name,
          type: 'info',
          debugMsg: {
            data,
            callbackValues:
              getCurComponentCallbackValues(containerId, callback, 'containers') || {},
          },
        });

      return data;
    } catch (error) {
      dispatchDebugScreenEvent<'error', 'dataRequest'>({
        type: 'error',
        id: containerId,
        dispatchFrom: 'dataRequest',
        name,
        debugMsg: {
          message: error.message,
          callbackValues: getCurComponentCallbackValues(containerId, callback, 'containers') || {},
          data: '暂未获取到数据',
        },
      });
      console.error(error);
      // 取消请求的时候会执行
      if (componentDataList[_key]) {
        componentDataList[_key] = null;
      }
      if (error && error.name === 'AbortError') {
        return;
      }
      dispatch({
        type: actionTypes.DATA_COMPONENT_GET_FAILURE,
        payload: {
          id: containerId,
          data: [],
          message: error.message,
        },
      });
    }
  };
}

export function filterResultUpdate({ id, data }) {
  return async (dispatch) => {
    dispatch({
      type: actionTypes.DATA_FILTER_RESULT_UPDATE,
      payload: { id, data },
    });
  };
}

const initialState: DataContainersType = {
  list: [],
  filterResult: {},
};

export default function reducer(state = initialState, { type, payload }): DataContainersType {
  switch (type) {
    case actionTypes.DATA_CONTAINER_LIST: {
      const addedDataContainers = payload.filter(
        (d) => !state.list.find((o: any) => o.id === d.id),
      );
      return { ...state, list: state.list.concat(addedDataContainers) };
    }
    case actionTypes.DATA_CONTAINER_ADD: {
      if (!payload) {
        return {
          ...state,
        };
      }

      const dataContainer = Array.isArray(payload) ? payload : [payload];

      return { ...state, list: dataContainer.concat(state.list) };
    }
    case actionTypes.DATA_CONTAINER_DELETE: {
      const newList = state.list.filter((d: any) => d.id !== payload);

      return { ...state, list: newList };
    }
    case actionTypes.DATA_CONTAINER_EVENTS_UPDATE:
    case actionTypes.DATA_CONTAINER_UPDATE: {
      const newList = state.list.map((d) => {
        if (d.id === payload.id) {
          return {
            ...d,
            ...payload,
          };
        }
        return d;
      });

      return {
        ...state,
        list: newList,
      };
    }
    case actionTypes.DATA_COMPONENT_GET_SUCCESS: {
      return {
        ...state,
        list: state.list.map((d) => {
          if (d.id === payload.id) {
            const _dataConfigs = {
              ...d.dataConfigs,
            };
            _dataConfigs[d.dataType]['data'] = payload.data;
            return {
              ...d,
              dataConfigs: _dataConfigs,
            };
          }
          return d;
        }),
      };
    }
    case actionTypes.DATA_COMPONENT_GET_FAILURE: {
      return {
        ...state,
        list: state.list.map((d) => {
          if (d.id === payload.id) {
            const _dataConfigs = {
              ...d.dataConfigs,
            };
            _dataConfigs[d.dataType]['data'] = payload.data;
            return {
              ...d,
              dataConfigs: _dataConfigs,
              errorMessage: payload.message,
            };
          }
          return d;
        }),
      };
    }
    case actionTypes.DATA_CONTAINER_CONFIG_UPDATE: {
      const { id } = payload;
      // param
      return {
        ...state,
        list: state.list.map((d) => {
          if (d.id === id) {
            return Object.assign(d, payload);
          }
          return d;
        }),
      };
    }
    case actionTypes.DATA_COMPONENT_FILTER_UPDATE: {
      // 过滤器被删除，更新绑定关系
      const { filterIds } = payload;
      return {
        ...state,
        list: state.list.map((d) => {
          return {
            ...d,
            filters: d.filters.filter((d) => !filterIds.includes(d.id)),
          };
        }),
      };
    }

    case actionTypes.DATA_FILTER_RESULT_UPDATE: {
      const { id, data } = payload;
      return {
        ...state,
        filterResult: {
          ...state.filterResult,
          [id]: data,
        },
      };
    }
    default:
      return state;
  }
}
