import { Method, AxiosRequestConfig, AxiosError, AxiosResponse } from 'axios';
import { RequestMethod } from './types';

export const isDate = (val?: any): boolean => {
  return toString.call(val) === '[object Date]';
};

export const isJSONstr = (str?: any): boolean => {
  try {
    return (
      typeof str === 'string' &&
      str.length &&
      (str = JSON.parse(str)) &&
      Object.prototype.toString.call(str) === '[object Object]'
    );
  } catch (error) {
    return false;
  }
};

export const encode = (val: string | number | boolean): string => {
  return encodeURIComponent(val)
    .replace(/%40/gi, '@')
    .replace(/%3A/gi, ':')
    .replace(/%24/g, '$')
    .replace(/%2C/gi, ',')
    .replace(/%20/g, '+')
    .replace(/%5B/gi, '[')
    .replace(/%5D/gi, ']');
};

/** 方法转换器 */
export const methodProcessor = (axiosMethod: Method): RequestMethod => {
  const upperCaseMethod = axiosMethod.toUpperCase();
  const repeatMethod = ['GET', 'POST', 'PUT', 'DELETE'];
  if (repeatMethod.includes(upperCaseMethod)) {
    return upperCaseMethod as RequestMethod;
  } else {
    throw new Error(`阿里小程序暂不支持${axiosMethod}方法`);
  }
};

/** url路径转换器 */
export const urlProcessor = (
  baseURL?: string,
  path?: string,
  params?: AxiosRequestConfig['params'],
  paramsSerializer?: AxiosRequestConfig['paramsSerializer']
): string => {
  let fullPath = /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(path)
    ? path
    : `${baseURL || ''}${path || ''}`;
  if (!params) {
    return fullPath;
  }
  const connector = fullPath.includes('?') ? '&' : '?';
  let serializedParam;
  if (paramsSerializer) {
    serializedParam = paramsSerializer(params);
    return `${fullPath}${connector}${serializedParam}`;
  }
  if (typeof params === 'string') {
    serializedParam = params;
  }
  if (typeof params === 'object') {
    serializedParam = Object.keys(params)
      .reduce((prev, key) => {
        const value = params[key as unknown as string];
        if (value === null || value === '') {
          return prev;
        }
        if (Array.isArray(value)) {
          value.forEach((item) => {
            prev.push(`${encode(key + '[]')}=${encode(item)}`);
          });
          return prev;
        }
        if (isDate(value)) {
          prev.push(`${encode(key)}=${encode(value.toISOString())}`);
          return prev;
        }
        if (typeof value === 'object') {
          prev.push(`${encode(key)}=${encode(value.toString())}`);
          return prev;
        }
        prev.push(`${encode(key)}=${encode(value)}`);
        return prev;
      }, [])
      .join('&');
  }
  return `${fullPath}${serializedParam ? connector : ''}${serializedParam}`;
};

export const dataProcessor = (data?: any) => {
  if (isJSONstr(data)) {
    return JSON.parse(data);
  }
  return data;
};

/** 正确返回处理器
 * 默认情况下 4xx、5xx请求在微信小程序中属于成功的返回
 * 因此这里需要对successResult做一次判断过滤
 *
 */
export const successResProcessor = (
  resolve: (val: AxiosResponse) => void,
  reject: (err: AxiosError) => void,
  res: any,
  config: AxiosRequestConfig,
  request: () => void
) => {
  const { data, status, headers, errorMessage, ...restResult } = res;
  const response = {
    data: data,
    status: status,
    statusText: errorMessage || 'request ok',
    headers: headers,
    config: config,
    request,
    ...restResult,
  };
  //保持和web逻辑一致
  if (config.validateStatus && !config.validateStatus(status)) {
    const error = new Error(
      `Request failed with status code ${status}`
    ) as AxiosError;
    error.config = config;
    error.request = request;
    error.response = response;
    error.isAxiosError = true;
    reject(error);
    return;
  }
  resolve(response);
};

/** 错误返回处理器 */
export const failResProcessor = (
  reject: (err: AxiosError) => void,
  res: any,
  config: AxiosRequestConfig,
  request: () => void
) => {
  const error = new Error(res.errMsg) as AxiosError;
  error.config = config;
  error.request = request;
  error.response = res;
  error.isAxiosError = true;
  reject(error);
};

export const headerProcessor = (header?: { [key: string]: any }) => {
  if (header?.referer) {
    delete header.referer;
  }
  return header;
};

export const dataTypeProcessor = (
  dataType?: AxiosRequestConfig['responseType']
): 'json' | 'text' | 'base64' | 'arraybuffer' => {
  if (!dataType || dataType === 'json') {
    return 'json';
  }
  if (dataType === 'text') {
    return 'text';
  }
  return 'arraybuffer';
};
