/* eslint-disable object-curly-newline */
import { isJSFunction } from '@alilc/lowcode-types';
import { transformArrayToMap, transformStringToFunction, clone } from './common';
import { jsonp, request, get, post } from './request';
import { DataSource, DataSourceItem } from '../types';
import logger from './logger';

const DS_STATUS = {
  INIT: 'init',
  LOADING: 'loading',
  LOADED: 'loaded',
  ERROR: 'error',
};

export class DataHelper {
  host: any;

  config: DataSource;

  parser: any;

  ajaxList: any[];

  ajaxMap: any;

  dataSourceMap: any;

  appHelper: any;

  constructor(comp: any, config: DataSource, appHelper: any, parser: any) {
    this.host = comp;
    this.config = config || {};
    this.parser = parser;
    this.ajaxList = config?.list || [];
    this.ajaxMap = transformArrayToMap(this.ajaxList, 'id');
    this.dataSourceMap = this.generateDataSourceMap();
    this.appHelper = appHelper;
  }

  // 重置config，dataSourceMap状态会被重置；
  resetConfig(config = {}) {
    this.config = config as DataSource;
    this.ajaxList = (config as DataSource)?.list || [];
    this.ajaxMap = transformArrayToMap(this.ajaxList, 'id');
    this.dataSourceMap = this.generateDataSourceMap();
    return this.dataSourceMap;
  }

  // 更新config，只会更新配置，状态保存；
  updateConfig(config = {}) {
    this.config = config as DataSource;
    this.ajaxList = (config as DataSource)?.list || [];
    const ajaxMap: any = transformArrayToMap(this.ajaxList, 'id');
    // 删除已经移除的接口
    Object.keys(this.ajaxMap).forEach((key) => {
      if (!ajaxMap[key]) {
        delete this.dataSourceMap[key];
      }
    });
    this.ajaxMap = ajaxMap;
    // 添加未加入到dataSourceMap中的接口
    this.ajaxList.forEach((item) => {
      if (!this.dataSourceMap[item.id]) {
        this.dataSourceMap[item.id] = {
          status: DS_STATUS.INIT,
          load: (...args: any) => {
            // @ts-ignore
            return this.getDataSource(item.id, ...args);
          },
        };
      }
    });
    return this.dataSourceMap;
  }

  generateDataSourceMap() {
    const res: any = {};
    this.ajaxList.forEach((item) => {
      res[item.id] = {
        status: DS_STATUS.INIT,
        load: (...args: any) => {
          // @ts-ignore
          return this.getDataSource(item.id, ...args);
        },
      };
    });
    return res;
  }

  updateDataSourceMap(id: string, data: any, error: any) {
    this.dataSourceMap[id].error = error || undefined;
    this.dataSourceMap[id].data = data;
    this.dataSourceMap[id].status = error ? DS_STATUS.ERROR : DS_STATUS.LOADED;
  }

  getInitData() {
    const initSyncData = this.parser(this.ajaxList).filter((item: DataSourceItem) => {
      if (item.isInit) {
        this.dataSourceMap[item.id].status = DS_STATUS.LOADING;
        return true;
      }
      return false;
    });
    // 所有 datasource 的 datahandler
    return this.asyncDataHandler(initSyncData).then((res) => {
      let { dataHandler } = this.config;
      if (isJSFunction(dataHandler)) {
        dataHandler = transformStringToFunction(dataHandler.value);
      }
      if (!dataHandler || typeof dataHandler !== 'function') return res;
      try {
        return (dataHandler as any).call(this.host, res);
      } catch (e) {
        console.error('请求数据处理函数运行出错', e);
      }
    });
  }

  getDataSource(id: string, params: any, otherOptions: any, callback: any) {
    const req = this.parser(this.ajaxMap[id]);
    const options = req.options || {};
    if (typeof otherOptions === 'function') {
      callback = otherOptions;
      otherOptions = {};
    }
    const { headers, ...otherProps } = otherOptions || {};
    if (!req) {
      console.warn(`getDataSource API named ${id} not exist`);
      return;
    }
    return this.asyncDataHandler([
      {
        ...req,
        options: {
          ...options,
          // 支持参数为array的情况，当参数为array时，不做参数合并
          params:
            Array.isArray(options.params) || Array.isArray(params)
              ? params || options.params
              : {
                ...options.params,
                ...params,
              },
          headers: {
            ...options.headers,
            ...headers,
          },
          ...otherProps,
        },
      },
    ])
      .then((res: any) => {
        try {
          callback && callback(res && res[id]);
        } catch (e) {
          console.error('load请求回调函数报错', e);
        }

        return res && res[id];
      })
      .catch((err) => {
        try {
          callback && callback(null, err);
        } catch (e) {
          console.error('load请求回调函数报错', e);
        }

        return err;
      });
  }

  asyncDataHandler(asyncDataList: any[]) {
    return new Promise((resolve, reject) => {
      const allReq = [];
      const doserReq: Array<{name: string; package: string; params: any }> = [];
      const doserList: string[] = [];
      const beforeRequest = this.appHelper && this.appHelper.utils && this.appHelper.utils.beforeRequest;
      const afterRequest = this.appHelper && this.appHelper.utils && this.appHelper.utils.afterRequest;
      const csrfInput = document.getElementById('_csrf_token');
      const _tb_token_ = (csrfInput as any)?.value;
      asyncDataList.forEach((req) => {
        const { id, type, options } = req;
        if (!id || !type || type === 'legao') return;
        if (type === 'doServer') {
          const { uri, params } = options || {};
          if (!uri) return;
          doserList.push(id);
          doserReq.push({ name: uri, package: 'cms', params });
        } else {
          allReq.push(req);
        }
      });

      if (doserReq.length > 0) {
        allReq.push({
          type: 'doServer',
          options: {
            uri: '/nrsService.do',
            cors: true,
            method: 'POST',
            params: {
              data: JSON.stringify(doserReq),
              _tb_token_,
            },
          },
        });
      }
      if (allReq.length === 0) resolve({});
      const res: any = {};
      // todo:
      Promise.all(
        allReq.map((item: any) => {
          return new Promise((resolve) => {
            const { type, id, dataHandler, options } = item;
            const doFetch = (type: string, options: any) => {
              this.fetchOne(type as any, options)
                ?.then((data: any) => {
                  if (afterRequest) {
                    this.appHelper.utils.afterRequest(item, data, undefined, (data: any, error: any) => {
                      fetchHandler(data, error);
                    });
                  } else {
                    fetchHandler(data, undefined);
                  }
                })
                .catch((err: Error) => {
                  if (afterRequest) {
                    // 必须要这么调用，否则beforeRequest中的this会丢失
                    this.appHelper.utils.afterRequest(item, undefined, err, (data: any, error: any) => {
                      fetchHandler(data, error);
                    });
                  } else {
                    fetchHandler(undefined, err);
                  }
                });
            };
            const fetchHandler = (data: any, error: any) => {
              if (type === 'doServer') {
                if (!Array.isArray(data)) {
                  data = [data];
                }
                doserList.forEach((id, idx) => {
                  const req: any = this.ajaxMap[id];
                  if (req) {
                    res[id] = this.dataHandler(id, req.dataHandler, data && data[idx], error);
                    this.updateDataSourceMap(id, res[id], error);
                  }
                });
              } else {
                res[id] = this.dataHandler(id, dataHandler, data, error);
                this.updateDataSourceMap(id, res[id], error);
              }
              resolve({});
            };

            if (type === 'doServer') {
              doserList.forEach((item) => {
                this.dataSourceMap[item].status = DS_STATUS.LOADING;
              });
            } else {
              this.dataSourceMap[id].status = DS_STATUS.LOADING;
            }
            // 请求切片
            if (beforeRequest) {
              // 必须要这么调用，否则beforeRequest中的this会丢失
              this.appHelper.utils.beforeRequest(item, clone(options), (options: any) => doFetch(type, options));
            } else {
              doFetch(type, options);
            }
          });
        }),
      )
        .then(() => {
          resolve(res);
        })
        .catch((e) => {
          reject(e);
        });
    });
  }

  // dataHandler todo:
  dataHandler(id: string, dataHandler: any, data: any, error: any) {
    if (isJSFunction(dataHandler)) {
      dataHandler = transformStringToFunction(dataHandler.value);
    }
    if (!dataHandler || typeof dataHandler !== 'function') return data;
    try {
      return dataHandler.call(this.host, data, error);
    } catch (e) {
      console.error(`[${ id }]单个请求数据处理函数运行出错`, e);
    }
  }

  fetchOne(type: DataSourceType, options: any) {
    // eslint-disable-next-line prefer-const
    let { uri, url, method = 'GET', headers, params, ...otherProps } = options;
    otherProps = otherProps || {};
    if (type === 'jsonp') {
      return jsonp(uri, params, otherProps);
    }

    if (type === 'fetch') {
      switch (method.toUpperCase()) {
        case 'GET':
          return get(uri, params, headers, otherProps);
        case 'POST':
          return post(uri, params, headers, otherProps);
        default:
          return request(uri, method, params, headers, otherProps);
      }
    }

    logger.log(`Engine default dataSource not support type:[${type}] dataSource request!`, options);
  }
}

type DataSourceType = 'fetch' | 'jsonp';
