import {
  Input,
  Select,
  Checkbox,
  Radio,
  InputNumber,
  TimePicker,
} from 'antd';
import moment from 'moment';
import { Range, RangeNumber, DatePickerLine, TimePickerLine } from './custom';
import {
  ConfigItem,
  FilterTabs,
  FilterComponentType,
  OptionsItem,
} from './filter.interface';
import CONFIG_CONST from './filter.config';

let DatePicker: any;
if(typeof window !== "undefined" ) {
  const Antd = require("antd");
  DatePicker = Antd.DatePicker;
}

// 开发环境, 生产环境这不抛出异常
const __dev__ =
  process && process.env && process.env['NODE_ENV'] !== 'prod';

interface MapObject {
  [propName: string]: unknown;
}

export function isNullOrEmpty(val: string | number | undefined): boolean {
  if (typeof val === 'undefined') return true;
  if (typeof val === 'string' && val.trim() === '') return true;
  if (val == null) return true;
  return false;
}

export function isObject(value: any): boolean {
  return Object.prototype.toString.call(value) === '[object Object]';
}

function isVaildDate(date: string): boolean {
  // 专门处理 "2020-02-23 12" 特殊情况
  let _date = date.split(' ');
  if (
    _date.length === 2 &&
    _date[1].trim() !== '' &&
    _date[1].indexOf(':') === -1
  ) {
    date += ':';
  }
  return new Date(date).toString() === 'Invalid Date';
}

/**
 * 判断 options 是否存在对应的 value值
 * @param arr
 * @param value
 */
function optionsHaveSameValue(
  arr: OptionsItem[],
  value: string | number,
): boolean {
  const result = arr.some((item: OptionsItem) => {
    if (item.value === value) return true;
    return false;
  });
  return result;
}

/**
 * 判断数组 son 是否是 parent 的子集
 * @param son
 * @param parent
 */
function isSubset(son: (string | number)[], parent: OptionsItem[]): boolean {
  const newParent = parent.map((el: OptionsItem) => el.value);
  return !!son.every((v: string | number) => newParent.includes(v));
}

// 校验属性, 属性值必须唯一
export const validateAttrnames = (
  configItems: ConfigItem[],
  tabs?: FilterTabs,
): void => {
  if (configItems.length === 0) {
    if (__dev__) {
      throw new Error('The "config" must be configured');
    }
  }
  // configItems 所有 apiParam 必须唯一
  const map: MapObject = {};
  configItems.forEach((configItem: ConfigItem, i: number): void => {
    if (configItem) {
      if (__dev__) {
        if (!configItem.apiParam) {
          throw new Error(
            `the \"apiParam\" is not configured in Item ${i}  of the \"configItem\" list`,
          );
        }
      }
      if (!map[configItem.apiParam]) {
        map[configItem.apiParam] = String(i);
      } else {
        if (__dev__) {
          throw new Error(
            `The \"${configItem.apiParam}\" already appears in the ${
              map[configItem.apiParam] as string
            } position, please change the apiParam`,
          );
        }
      }
    }
  });

  // 如果配置 tabs，则 apiParam, 必须唯一
  if (
    (tabs && tabs.apiParam && isNullOrEmpty(tabs.apiParam)) ||
    (tabs && typeof tabs.apiParam === 'undefined')
  ) {
    if (__dev__) {
      throw new Error('The "apiParam" must be configured in tabs');
    }
  }

  // 如果配置 tabs，则 options 必须配置
  if (tabs && !Array.isArray(tabs.options)) {
    if (__dev__) {
      throw new Error(
        'The "options" must be configured in tabs, must be an array',
      );
    }
  }

  // 属性不能重复
  if (tabs && tabs.apiParam && map[tabs.apiParam]) {
    if (__dev__) {
      throw new Error(
        `The ${tabs.apiParam} already appears in the ${
          map[tabs.apiParam] as string
        } position, please change the apiParam`,
      );
    }
  }
};

// 校验并初始化默认值
export const validateDefaultValues = (configItems: ConfigItem[]): MapObject => {
  const map: MapObject = {};
  configItems.forEach((configItem: ConfigItem): void => {
    if (
      configItem &&
      configItem.originalProps &&
      configItem.originalProps.value !== undefined
    ) {
      const { value, format } = configItem.originalProps;
      if (configItem.type === 'Input') {
        if (!(typeof value === 'string' || typeof value === 'number')) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\", the value \"${
                (value as unknown) as string | number
              }\" must be string or number`,
            );
          }
        }
        map[configItem.apiParam] = value;
      } else if (
        configItem.type === 'DatePicker' ||
        configItem.type === 'TimePicker'
      ) {
        if (
          configItem.type === 'DatePicker' &&
          typeof value === 'string' &&
          isVaildDate(value as string)
        ) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\", the value \"${value}\" must be a valid time string`,
            );
          }
        }
        if (
          configItem.type === 'TimePicker' &&
          typeof value === 'string' &&
          !format
        ) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\", the \"format\" must be configured`,
            );
          }
        }
        if (typeof value === 'string') {
          if (format) {
            map[
              `${CONFIG_CONST.DATE_COMPONENT_MARK}${configItem.apiParam}`
            ] = moment(value, format);
          } else {
            map[
              `${CONFIG_CONST.DATE_COMPONENT_MARK}${configItem.apiParam}`
            ] = moment(value);
          }
        } else {
          map[
            `${CONFIG_CONST.DATE_COMPONENT_MARK}${configItem.apiParam}`
          ] = value;
        }
      } else if (
        configItem.type === 'DatePickerRangePicker' ||
        configItem.type === 'TimePickerRangePicker' ||
        configItem.type === 'DatePickerLine' ||
        configItem.type === 'TimePickerLine'
      ) {
        if (!Array.isArray(value)) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\", the value \"\[${value}\]\" must be Array`,
            );
          }
        }
        if (Array.isArray(value)) {
          if (value.length !== 2) {
            if (__dev__) {
              throw new Error(
                `For components \"${
                  configItem.type
                }\", the value ${JSON.stringify(value)} length must be 2`,
              );
            }
          }
          // 时间戳格式
          if (
            configItem.type === 'DatePickerRangePicker' ||
            configItem.type === 'DatePickerLine'
          ) {
            if (
              typeof (value as string[])[0] === 'string' &&
              isVaildDate((value as string[])[0])
            ) {
              if (__dev__) {
                throw new Error(
                  `For components \"${configItem.type}\", the \"${value[0]}\" is an invalid time string`,
                );
              }
            }
            if (
              typeof (value as string[])[1] === 'string' &&
              isVaildDate((value as string[])[1])
            ) {
              if (__dev__) {
                throw new Error(
                  `For components \"${configItem.type}\", the \"${value[1]}\" is an invalid time string`,
                );
              }
            }
          }
          if (
            (configItem.type === 'TimePickerRangePicker' ||
              configItem.type === 'TimePickerLine') &&
            !format
          ) {
            if (__dev__) {
              throw new Error(
                `For components \"${configItem.type}\", the \"format\" must be configured`,
              );
            }
          }
          if (format) {
            map[`${CONFIG_CONST.DATE_COMPONENT_MARK}${configItem.apiParam}`] = [
              typeof (value as string[])[0] === 'string'
                ? moment(value[0], format)
                : value[0],
              typeof (value as string[])[1] === 'string'
                ? moment(value[1], format)
                : value[1],
            ];
          } else {
            map[`${CONFIG_CONST.DATE_COMPONENT_MARK}${configItem.apiParam}`] = [
              typeof (value as string[])[0] === 'string'
                ? moment(value[0])
                : value[0],
              typeof (value as string[])[0] === 'string'
                ? moment(value[1])
                : value[1],
            ];
          }
        }
      } else if (configItem.type === 'InputNumber') {
        if (typeof value !== 'number') {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\", the value \"${
                (value as unknown) as number
              }\" must be number`,
            );
          }
        }
        if (configItem.originalProps.min !== undefined) {
          if (typeof configItem.originalProps.min !== 'number') {
            if (__dev__) {
              throw new Error(
                `For components \"${configItem.type}\", the min \"${
                  configItem.originalProps.min as number
                }\" must be number`,
              );
            }
          }
          if (value < configItem.originalProps.min) {
            if (__dev__) {
              throw new Error(
                `For components \"${configItem.type}\", the value \"${value}\" must be greater than or equal to the \"${configItem.originalProps.min}\"`,
              );
            }
          }
        }
        if (configItem.originalProps.max !== undefined) {
          if (typeof configItem.originalProps.max !== 'number') {
            if (__dev__) {
              throw new Error(
                `For components \"${configItem.type}\",  the min \"${
                  configItem.originalProps.max as number
                }\" must be number`,
              );
            }
          }
          if (value > configItem.originalProps.max) {
            if (__dev__) {
              throw new Error(
                `For components \"${configItem.type}\", the value \"${value}\" must be less than or equal to the \"${configItem.originalProps.max}\"`,
              );
            }
          }
        }
        map[configItem.apiParam] = value;
      } else if (configItem.type === 'Select') {
        map[configItem.apiParam] = value;
      } else if (configItem.type === 'Checkbox') {
        if (!configItem.options) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\", options must be configured`,
            );
          }
        }
        // 必须是 options 的子集
        if (
          !isSubset(
            value as (string | number)[],
            configItem.options as OptionsItem[],
          )
        ) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\", array \"\[${
                (value as unknown) as string
              }\]\" must be a subset of array \"options\"`,
            );
          }
        }
        map[configItem.apiParam] = value;
      } else if (configItem.type === 'Radio') {
        if (!configItem.options) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\", options must be configured`,
            );
          }
        }
        if (
          !optionsHaveSameValue(
            configItem.options as OptionsItem[],
            (value as unknown) as string,
          )
        ) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\", \"${
                (value as unknown) as string
              }\" must be the value value of an item in the \"options\" array`,
            );
          }
        }
        map[configItem.apiParam] = value;
      } else if (configItem.type === 'Range') {
        if (!Array.isArray(value)) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\",  value \"\[${value}\]\" must be Array`,
            );
          }
        }
        if ((value as (number | string)[]).length !== 2) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\",  value ${JSON.stringify(
                value,
              )} length must be 2`,
            );
          }
        }
        map[configItem.apiParam] = value;
      } else if (configItem.type === 'RangeNumber') {
        if (!Array.isArray(value)) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\",  value \"\[${value}\]\" must be Array`,
            );
          }
        }
        if ((value as (number | string)[]).length !== 2) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\",  value ${JSON.stringify(
                value,
              )} length must be 2`,
            );
          }
        }
        if (typeof (value as (number | string)[])[0] !== 'number') {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\",  The first \"${
                (value as (number | string)[])[0]
              }\" must be a number `,
            );
          }
        }
        if (typeof (value as (number | string)[])[1] !== 'number') {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\",  The second \"${
                (value as (number | string)[])[1]
              }\" must be a number `,
            );
          }
        }
        if (
          (value as (number | string)[])[0] >= (value as (number | string)[])[1]
        ) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\", the value \"${
                (value as (number | string)[])[0]
              }\" must be less than the value \"${
                (value as (number | string)[])[1]
              }\"`,
            );
          }
        }
        map[configItem.apiParam] = value;
      }
    }
  });
  return map;
};

// 校验 placeholder
export const validatePlaceholders = (configItems: ConfigItem[]): void => {
  const placeholderIsString = [
    'Input',
    'DatePicker',
    'InputNumber',
    'Select',
    'TimePicker',
  ];
  const placeholderIsArray = [
    'DatePickerRangePicker',
    'Range',
    'RangeNumber',
    'TimePickerRangePicker',
    'DatePickerLine',
    'TimePickerLine',
  ];
  configItems.forEach((configItem: ConfigItem): void => {
    if (
      configItem &&
      configItem.originalProps &&
      configItem.originalProps.placeholder !== undefined
    ) {
      const { placeholder } = configItem.originalProps;
      if (
        placeholderIsString.includes(configItem.type as FilterComponentType)
      ) {
        if (Array.isArray(placeholder)) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\", the placeholder \"${
                (placeholder as unknown) as string
              }\" must be string, no array`,
            );
          }
        }
        if (!(typeof placeholder === 'string')) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\", the placeholder \"${
                placeholder as string[]
              }\" must be string`,
            );
          }
        }
      } else if (
        placeholderIsArray.includes(configItem.type as FilterComponentType)
      ) {
        if (!Array.isArray(placeholder)) {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\", the placeholder \"${placeholder}\" must be Array`,
            );
          }
        }
        if ((placeholder as string[]).length !== 2) {
          if (__dev__) {
            throw new Error(
              `For components \"${
                configItem.type
              }\", the placeholder ${JSON.stringify(
                placeholder,
              )} length must be 2`,
            );
          }
        }
        if (typeof placeholder[0] !== 'string') {
          if (__dev__) {
            throw new Error(
              `For components \"${configItem.type}\", the first placeholder \"${
                placeholder[0] as string
              }\" must be string`,
            );
          }
        }
        if (typeof placeholder[1] !== 'string') {
          if (__dev__) {
            throw new Error(
              `For components \"${
                configItem.type
              }\", the second placeholder \"${
                placeholder[1] as string
              }\" must be string`,
            );
          }
        }
      }
    }
  });
};

/**
 * 重制搜索值
 * @param configItems
 */
export const resetValues = (
  configItems: ConfigItem[],
): { [rest: string]: unknown } => {
  const map: MapObject = {};
  configItems.forEach((configItem: ConfigItem): void => {
    if (configItem) {
      if (configItem._moment) {
        map[
          `${CONFIG_CONST.DATE_COMPONENT_MARK}${configItem.apiParam}`
        ] = undefined;
        map[configItem.apiParam] = undefined;
      } else {
        map[configItem.apiParam] = undefined;
      }
    }
  });
  return map;
};

/**
 * 根据 type 返回对应的组件
 * @param configItems
 */
export const transformToComponent = (
  configItems: ConfigItem[],
): ConfigItem[] => {
  configItems = configItems.filter((configItem: ConfigItem) => !!configItem);
  configItems = configItems.map(
    (configItem: ConfigItem): ConfigItem => {
      switch (configItem.type) {
        case 'Range':
          return {
            ...configItem,
            _type: Range,
            _custom: true, // 是自定义组件
            _moment: false, // 日期组件
          };
        case 'RangeNumber':
          return {
            ...configItem,
            _type: RangeNumber,
            _custom: true,
            _moment: false,
          };
        case 'Input':
          return {
            ...configItem,
            _type: Input,
            _custom: false,
            _moment: false,
          };
        case 'InputNumber':
          return {
            ...configItem,
            _type: InputNumber,
            _custom: false,
            _moment: false,
          };
        case 'DatePicker':
          // 如果没有传style，则取默认值，填充100%
          configItem.originalProps = {
            style: { width: '100%' },
            ...configItem.originalProps,
          };
          return {
            ...configItem,
            _type: DatePicker,
            _custom: false,
            _moment: true,
          };
        case 'DatePickerRangePicker':
          configItem.originalProps = {
            style: { width: '100%' },
            ...configItem.originalProps,
          };
          return {
            ...configItem,
            _type: DatePicker.RangePicker,
            _custom: false,
            _moment: true,
          };
        case 'Select':
          return {
            ...configItem,
            _type: Select,
            _custom: false,
            _moment: false,
          };
        case 'Checkbox':
          return {
            ...configItem,
            _type: Checkbox,
            _custom: false,
            _moment: false,
          };
        case 'Radio':
          return {
            ...configItem,
            _type: Radio,
            _custom: false,
            _moment: false,
          };
        case 'TimePicker':
          configItem.originalProps = {
            style: { width: '100%' },
            ...configItem.originalProps,
          };
          return {
            ...configItem,
            _type: TimePicker,
            _custom: false,
            _moment: true,
          };
        case 'TimePickerRangePicker':
          configItem.originalProps = {
            style: { width: '100%' },
            ...configItem.originalProps,
          };
          return {
            ...configItem,
            _type: TimePicker.RangePicker,
            _custom: false,
            _moment: true,
          };
        case 'DatePickerLine':
          return {
            ...configItem,
            _type: DatePickerLine,
            _custom: true,
            _moment: true,
          };
        case 'TimePickerLine':
          return {
            ...configItem,
            _type: TimePickerLine,
            _custom: true,
            _moment: true,
          };
        default:
          return {
            ...configItem,
            apiParam: `${CONFIG_CONST.EXTERNAL_RENDER_MARK}${configItem.apiParam}`,
            _externalRender: true,
          };
      }
    },
  );

  return configItems;
};

/**
 * 处理日期，时间范围, 默认返回毫秒数
 * @param configItems
 * @param values
 */
export const handleDateOrRangePicker = (
  configItems: ConfigItem[],
  values: any,
): void => {
  const singlePicker = ['DatePicker', 'TimePicker'];
  const doublePicker = [
    'DatePickerRangePicker',
    'DatePickerLine',
    'TimePickerRangePicker',
    'TimePickerLine',
  ];
  configItems.forEach((configItem: ConfigItem) => {
    if (configItem) {
      if (
        [...singlePicker, ...doublePicker].includes(
          configItem.type as FilterComponentType,
        )
      ) {
        if (configItem.originalProps && configItem.originalProps.format) {
          const { format } = configItem.originalProps;
          const originalName = `${CONFIG_CONST.DATE_COMPONENT_MARK}${configItem.apiParam}`;
          if (singlePicker.includes(configItem.type as FilterComponentType)) {
            if (values[originalName]) {
              if (typeof values[originalName] === 'string') {
                values[configItem.apiParam] = values[originalName];
              } else {
                values[configItem.apiParam] = values[originalName].format(
                  format,
                );
              }
            }
            delete values[originalName];
          }
          if (
            doublePicker.includes(configItem.type as FilterComponentType) &&
            values[originalName]
          ) {
            values[configItem.apiParam] = [];
            if (values[originalName] && values[originalName][0]) {
              if (typeof values[originalName][0] === 'string') {
                values[configItem.apiParam][0] = values[originalName][0];
              } else {
                values[configItem.apiParam][0] = values[originalName][0].format(
                  format,
                );
              }
            }
            if (values[originalName] && values[originalName][1]) {
              if (typeof values[originalName][1] === 'string') {
                values[configItem.apiParam][1] = values[originalName][1];
              } else {
                values[configItem.apiParam][1] = values[originalName][1].format(
                  format,
                );
              }
            }
            delete values[originalName];
          }
        } else {
          const format =
            configItem.type === 'DatePickerRangePicker' ||
            configItem.type === 'DatePickerLine'
              ? 'YYYY-MM-DD HH:mm:ss'
              : 'HH:mm';
          const originalName = `${CONFIG_CONST.DATE_COMPONENT_MARK}${configItem.apiParam}`;
          if (singlePicker.includes(configItem.type as FilterComponentType)) {
            if (values[originalName]) {
              if (typeof values[originalName] === 'string') {
                values[configItem.apiParam] = values[originalName];
              } else {
                values[configItem.apiParam] = values[originalName].format(
                  format,
                );
              }
              delete values[originalName];
            }
          }
          if (
            doublePicker.includes(configItem.type as FilterComponentType) &&
            values[originalName]
          ) {
            values[configItem.apiParam] = [];
            if (values[originalName] && values[originalName][0]) {
              if (typeof values[originalName][0] === 'string') {
                values[configItem.apiParam][0] = values[originalName][0];
              } else {
                values[configItem.apiParam][0] = values[originalName][0].format(
                  format,
                );
              }
            }
            if (values[originalName] && values[originalName][1]) {
              if (typeof values[originalName][1] === 'string') {
                values[configItem.apiParam][1] = values[originalName][1];
              } else {
                values[configItem.apiParam][1] = values[originalName][1].format(
                  format,
                );
              }
            }
            delete values[originalName];
          }
        }
      }
    }
  });
};

/**
 * -列，两列，三列布局
 * 配置动态列表  992
 * @param i 下标
 * @param w 自适应屏宽
 */
export const adaptiveCol = (i: number, w: number): unknown => {
  let config = {};
  i += 1;
  if (
    (i % 3 === 1 && w >= CONFIG_CONST.SCREEN_WIDTH_992) ||
    (i % 2 === 1 && w < CONFIG_CONST.SCREEN_WIDTH_992)
  ) {
    config = {
      lg: 8,
      md: 12,
      sm: 24,
    };
  } else if (
    (i % 3 === 2 && w >= CONFIG_CONST.SCREEN_WIDTH_992) ||
    (i % 2 === 0 && w < CONFIG_CONST.SCREEN_WIDTH_992)
  ) {
    config = {
      lg: {
        span: 7,
        offset: 1,
      },
      md: {
        span: 11,
        offset: 1,
      },
      sm: 24,
    };
  } else if (i % 3 === 0 && w >= CONFIG_CONST.SCREEN_WIDTH_992) {
    config = {
      lg: {
        span: 7,
        offset: 1,
      },
      md: {
        span: 24,
      },
      sm: 24,
    };
  }
  return config;
};

/**
 * // 最后一列 列宽
 * @param isOpen 是否展开状态
 * @param w 屏幕宽度
 * @param configItems 配置列表
 */
export const operationColSpanNumber = (
  isOpen: boolean,
  w: number,
  configItems: string | any[],
): number => {
  if (!isOpen) {
    // 全部展开状态
    if (w >= CONFIG_CONST.SCREEN_WIDTH_992 && configItems.length % 3 === 0) {
      // 3
      return 24;
    } else if (
      w >= CONFIG_CONST.SCREEN_WIDTH_992 &&
      (configItems.length - 1) % 3 === 0
    ) {
      // 1
      return 16;
    } else if (
      w >= CONFIG_CONST.SCREEN_WIDTH_992 &&
      (configItems.length - 2) % 3 === 0
    ) {
      // 2
      return 8;
    } else if (
      w >= CONFIG_CONST.SCREEN_WIDTH_768 &&
      w < CONFIG_CONST.SCREEN_WIDTH_992 &&
      configItems.length % 2 === 0
    ) {
      return 24;
    } else if (
      w >= CONFIG_CONST.SCREEN_WIDTH_768 &&
      w < CONFIG_CONST.SCREEN_WIDTH_992 &&
      configItems.length % 2 === 1
    ) {
      return 12;
    } else {
      return 24;
    }
  } else {
    // 超过5个时，适配的 Col
    if (w >= CONFIG_CONST.SCREEN_WIDTH_992) {
      return 8;
    } else if (
      w >= CONFIG_CONST.SCREEN_WIDTH_768 &&
      w < CONFIG_CONST.SCREEN_WIDTH_992
    ) {
      return 12;
    } else {
      return 24;
    }
  }
};

/**
 * 去除对象中的 undefined
 * @param values
 */
export const removeUndefined = (values: {
  [propName: string]: string | number | (string | number)[] | undefined;
}): void => {
  for (const key in values) {
    if (Object.prototype.hasOwnProperty.call(values, key)) {
      if (values[key] === undefined) {
        delete values[key];
      }
    }
  }
};
