import produce from 'immer';
import { request } from 'utils/fetch';
import { api } from 'constants/config';
import { BuriedPointLog } from './db/buriedPointLog';
import qs from 'qs';
import { nest } from 'd3';
import {
  EditionListType,
  ResultLocalProductionType,
  ResultSaasProductionType,
  TanantListType,
} from 'models/user.types';

const buriedPointLog = new BuriedPointLog();

export function isEmptyObject(obj: any) {
  return !Object.keys(obj).length;
}

export function getValueByKey(obj: any, key: string) {
  if (key == null || key === '') {
    return;
  }

  const keys = key.split('.');

  if (keys.length === 0) {
    return;
  }

  try {
    return keys.reduce((all, item) => all[item], obj);
  } catch (error) {
    return;
  }
}

export function dataURItoBlob(base64Data: string) {
  let byteString: string;
  if (base64Data.split(',')[0].indexOf('base64') >= 0) {
    byteString = atob(base64Data.split(',')[1]);
  } else {
    byteString = unescape(base64Data.split(',')[1]);
  }

  const mimeString = base64Data.split(',')[0].split(':')[1].split(';')[0];
  const ia = new Uint8Array(byteString.length);
  for (let i = 0; i < byteString.length; i++) {
    ia[i] = byteString.charCodeAt(i);
  }
  return new Blob([ia], { type: mimeString });
}

export function jsonToFormdata(obj: Object) {
  if (!obj || typeof obj !== 'object') {
    return;
  }

  const formData = new FormData();
  Object.keys(obj).forEach((item) => {
    formData.append(item, obj[item]);
  });

  return formData;
}

export const PartialStateShare = (function () {
  const cache = new Map([]);
  const callbacks = new Map<string, Function[]>([]);

  return {
    on: (key: string, cb: Function) => {
      const list = callbacks.get(key);
      if (list) {
        list.push(cb);
      } else {
        callbacks.set(key, [cb]);
      }
    },
    off: (key: string, cb: Function) => {
      const list = callbacks.get(key);
      if (!list) {
        return;
      }

      callbacks.set(
        key,
        list.filter((_cb) => _cb !== cb),
      );
    },
    emit: (key: string, value: any) => {
      cache.set(key, value);
      if (!callbacks.get(key)) {
        return;
      } else {
        (callbacks.get(key) as Function[]).forEach((cb) => {
          cb.call(null, value);
        });
      }
    },
    get: (key: string): any => {
      return cache.get(key);
    },
    delete: (key: string) => {
      cache.delete(key);
      const list = callbacks.get(key);
      if (!list) {
        return;
      }

      list.forEach((cb) => {
        this.off(key, cb);
      });
      callbacks.delete(key);
    },
  };
})();

export function clearClipboard() {
  const input = document.createElement('input');
  input.value = ' ';
  document.body.appendChild(input);
  input.select();
  document.execCommand('Copy');
  input.parentNode?.removeChild(input);
}

export function deepFind(data: any[] = [], predicate: (arg) => boolean, deepKey: string) {
  return data.reduce(
    (all, item) =>
      predicate(item)
        ? all.concat(item)
        : Array.isArray(item[deepKey])
        ? all.concat(deepFind(item[deepKey], predicate, deepKey))
        : all,
    [],
  );
}

export function deepFindOne(data = [], predicate: (arg) => boolean, deepKey = 'components') {
  for (let i = 0; i < data.length; i++) {
    const item = data[i];
    if (predicate(item)) {
      return item;
    } else {
      if (Array.isArray(item[deepKey])) {
        const result = deepFindOne(item[deepKey], predicate, deepKey);
        if (result) {
          return result;
        }
      }
    }
  }
}

// TODO深度循环
export function deepMap(data: any = [], predicate: (arg) => any, deepKey = 'components') {
  return data.reduce(
    (all, item) =>
      // eslint-disable-next-line no-prototype-builtins
      item.hasOwnProperty(deepKey)
        ? all.concat({ ...item, [deepKey]: deepMap(item[deepKey], predicate, deepKey) })
        : all.concat(predicate(item)),
    [],
  );
}

export function isAppPage(): boolean {
  return /\/app\/edit\/[0-9]+\/[0-9]+/.test(window.location.pathname);
}

export function isNodePage(): boolean {
  return /\/node\/view\/([0-9]+)$/.test(window.location.pathname);
}
export function isEditPage(): boolean {
  return /\/create\/([0-9]+)$/.test(window.location.pathname);
}
export function isShareScreenPage() {
  return /\/shareScreen\/[0-9a-zA-Z]{1,}/.test(window.location.pathname);
}

export function isPanel(id: string): boolean {
  return /^panel_/.test(id) || /^\$panel\((panel_[0-9]+)\)/.test(id);
}

export function isGroup(id: string): boolean {
  return /^group_/.test(id) || /^\$group\((group_\w+)\)/.test(id);
}

export function isState(id: string): boolean {
  return /^state_/.test(id) || /^\$state\(([0-9]+)\)/.test(id);
}

export function isScreen(id: string): boolean {
  return /^screen_/.test(id) || /^\$screen\(([0-9]+)\)/.test(id);
}

export function isComponent(id: number | string): boolean {
  return typeof id === 'number' || /^\$component\(([0-9]+)\)/.test(id);
}

export function isRefScreen(id: string): boolean {
  return /^ref_/.test(id) || /^\$panel\((ref_[0-9]+)\)/.test(id);
}

export function isGeneralPanel(id: string) {
  return isPanel(id) || isRefScreen(id);
}

export function raf(cb: Function) {
  let next: any = null;
  let stop = true;
  let rid;
  function run() {
    rid = requestAnimationFrame(() => {
      if (!next) {
        stop = true;
        return;
      }
      stop = false;
      const _next = next;
      next = null;
      try {
        _next();
      } catch (e) {
        console.error(e);
      } finally {
        run();
      }
    });
  }

  function _raf(...args) {
    // eslint-disable-next-line prefer-spread
    next = () => cb.apply(null, args);
    stop && run();
  }

  _raf.destory = () => {
    cancelAnimationFrame(rid);
  };
  return _raf;
}

export function omitUndefined(obj: Object) {
  const newObj = {};
  for (const key in obj) {
    if (obj[key] !== undefined) {
      newObj[key] = obj[key];
    }
  }

  return newObj;
}

export function generateId() {
  return Math.random().toString(36).substr(3, 20);
}

export function toNumber(value: string | number) {
  return isNaN(+value) ? NaN : +value;
}

// 根据key，更新对象或者数组的值
export function updateDataByKey({
  key,
  value,
  paths = [],
  data,
}: {
  key: string | number;
  value: any;
  paths: (string | number)[];
  data: any;
}) {
  if (key === undefined && paths.length === 0) {
    return value;
  } else {
    return produce(data, (newData) => {
      if (key === undefined) {
        key = paths[paths.length - 1];
        paths = paths.slice(0, paths.length - 1);
      }

      const origin = paths.reduce((all, cur) => {
        if (all[cur] === undefined) {
          all[cur] = {};
        }
        return all[cur];
      }, newData);

      // value 不传,表示删除
      if (value === undefined) {
        // 数组的删除用splice，对象的删除用delete
        if (typeof key === 'number') {
          origin.splice(key, 1);
        } else {
          delete origin[key];
        }
      } else {
        origin[key] = value;
      }
    });
  }
}

export function redirectToUpgrade(skuId: number, saas?: string) {
  const s = saas || 'saas_advanced';
  let queryUrl = `/order/upgrade?edition=${s}`;
  if (skuId && skuId <= 3) {
    queryUrl = `/order?edition=${s}`;
  }
  redirect(window.appConfig.HOME_URL + `${queryUrl}`, '_blank');
}

export function redirect(url: string, target: '_blank' | '_self') {
  const a = document.createElement('a');
  a.href = url;
  a.target = target;
  if (target === '_blank') {
    a.rel = 'noopener';
  }
  a.click();
}

export function redirectToLogin() {
  const tenantId = localStorage.getItem('lastTenantId');
  const redirectUrl = window.location.href + (tenantId ? `&tenantId=${tenantId}` : '');
  if (window.appConfig.LOGIN_URL) {
    window.location.href = window.appConfig.LOGIN_URL.includes('{REDIRECT_URL}')
      ? window.appConfig.LOGIN_URL.replace('{REDIRECT_URL}', redirectUrl)
      : window.appConfig.LOGIN_URL;
  } else {
    window.location.href = `/login?redirectUrl=${redirectUrl}`;
  }
}

export function isLowVersion(current: string, latest: string): boolean {
  if (!latest || !current || current === latest) {
    return false;
  }

  const currentArr = current.split('.');
  const latestArr = latest.split('.');
  const maxL = Math.max(currentArr.length, latestArr.length);
  let isLow = false;
  let i = 0;
  while (i <= maxL) {
    if (!currentArr[i] || +currentArr[i] - +latestArr[i] < 0) {
      isLow = true;
      break;
    } else if (!latestArr[i] || +currentArr[i] - +latestArr[i] > 0) {
      break;
    }
    i++;
  }
  return isLow;
}

// 第一个参数为日期，第二个参数为年月日分割格式 '/'或'-'
export function format(Date: Date, str = '-') {
  const obj = {
    Y: Date.getFullYear(),
    M: Date.getMonth() + 1,
    D: Date.getDate(),
    H: Date.getHours(),
    Mi: Date.getMinutes(),
    S: Date.getSeconds(),
  };

  return (
    obj.Y +
    str +
    supplement(obj.M) +
    str +
    supplement(obj.D) +
    ' ' +
    supplement(obj.H) +
    ':' +
    supplement(obj.Mi) +
    ':' +
    supplement(obj.S)
  );
}

// 位数不足两位补全0
function supplement(nn: number) {
  return nn < 10 ? '0' + nn : nn;
}

export function group<T>(data: T[], func: (arg: T) => string) {
  const result: { key: string; value: T[] }[] = [];

  data.map((d) => {
    const key = func(d) || '';
    const index = result.findIndex((o) => o.key === key);
    if (index === -1) {
      result.push({
        key: key,
        value: [d],
      });
    } else {
      result[index] = {
        key: key,
        value: result[index].value.concat(d),
      };
    }
  });

  return result;
}

/**
 * @description: 收集数据
 * @param {data}: 数据模型由埋点信息决定
 * @return {void}: 无返回值
 */
export function collectData<T>(data: T): void {
  if (!window.appConfig.LOCAL) {
    const createTime = Date.now();
    try {
      buriedPointLog.addDB({
        ...data,
        createTime,
      });
    } catch (err) {
      // console.log(err);
    }
  }
}

/**
 * @description: 上传本地存储数据，不允许在工作台内做上传动作
 * @param {*}
 * @return {*}
 */
export async function collectDataFetch<T>(data?: T): Promise<void> {
  if (!window.appConfig.LOCAL && !isNodePage() && !isEditPage() && !isAppPage()) {
    const body = data ? [data] : buriedPointLog.db && (await buriedPointLog.readAll());
    if (body && body.length) {
      const result = await request(
        api.easyvLog,
        undefined,
        undefined,
        JSON.stringify(
          body.map((d) => {
            const { id, ...data } = d;
            return data;
          }),
        ),
      );
      if (!data) {
        result && buriedPointLog.clear();
      }
    }
  }
}

export function isJsonString(str: string) {
  if (typeof str == 'string') {
    try {
      const obj = JSON.parse(str);
      if (typeof obj == 'object' && obj) {
        return true;
      } else {
        return false;
      }
    } catch (e) {
      console.log('error：' + str + '!!!' + e);
      return false;
    }
  }
  console.log('It is not a string!');
  return false;
}

export function parseSearchParams() {
  const searchParamsString = window.location.search.substring(1);
  return searchParamsString ? qs.parse(searchParamsString) : {};
}

export function getSize(size) {
  return size / 1024 < 1024 ? `${Math.floor(size / 1024)}KB` : `${Math.floor(size / 1024 / 1024)}M`;
}

export function objectValueTrim(value) {
  const _value = value || {};
  for (const key in _value) {
    // eslint-disable-next-line no-prototype-builtins
    if (_value.hasOwnProperty(key) && typeof _value[key] === 'string') {
      _value[key] = _value[key].trim();
    }
  }
  return _value;
}

export function getGroupedCategorys(data) {
  let result: any[] = [];
  const nestedCategorys = nest()
    .key((d) => d.parentId)
    .entries(data);

  nestedCategorys
    .concat()
    .sort((a, b) => a.key - b.key)
    .map((d) => {
      if (d.key == 0) {
        result = d.values;
      } else {
        result = result.map((v) => {
          if (v.id == d.key) {
            return {
              ...v,
              children: d.values,
            };
          } else {
            return v;
          }
        });
      }
    });

  result = result.map((d) => (d.children ? d : { ...d, children: [] }));

  return result;
}

/**
 *
 * @param skuId 根据skuId和是否是renew获取saas对应产品的中文名和英文名数组
 * @returns
 */
export const resultSaasProdcutionName: ResultSaasProductionType = (skuId, isRenew = false) => {
  const saasProductionList: EditionListType = {
    1: ['SaaS 试用版', 'saas_basic'],
    2: ['SaaS 高级版', 'saas_advanced'],
    3: ['SaaS 尊享版', 'saas_vip'],
    4: ['SaaS 基础版', 'saas_basic'],
    5: isRenew ? ['Renew 企业版', 'saas_advanced'] : ['SaaS 高级版', 'saas_advanced'],
    6: isRenew ? ['Renew 开发版', 'saas_vip'] : ['SaaS 尊享版', 'saas_vip'],
    7: ['个人版', 'saas_personal'],
    8: ['个人版', 'saas_personal'],
  };
  return saasProductionList[skuId];
};

/**
 *
 * @param isRenew 是否是renew账号
 * @param id 独立部署产品ID
 * @returns 获取各个产品续费跳转url
 */
export const getRenewalRedirectUrl = (
  isRenew: boolean,
  id?: 'saas_basic' | 'saas_advanced' | 'saas_vip' | 'saas_personal',
) => {
  if (typeof id === 'string') {
    return isRenew
      ? window.appConfig.HOME_URL + `/order/renew?edition=${id.replace('saas', 'renew')}`
      : window.appConfig.HOME_URL + `/order/renew?edition=${id}`;
  }
};

/**
 *
 * @param type 根据type获取独立部署产品中文名
 * @returns
 */
export const resultLocalProductionName: ResultLocalProductionType = (type) => {
  const LocalProductionListType: TanantListType = {
    education: '基础版',
    community: '开发者版',
    enterprise: '企业版',
    lifelong: '开发者版',
  };
  return LocalProductionListType[type];
};

/**
 * @description 校验pwd是否位英文或数字且长度为6-20位
 * @param pwd
 * @returns
 */
export const pwdValidate = (pwd: string) => {
  return /^[0-9a-zA-Z]{6,20}$/.test(pwd);
};

export function scrollChangeSelectBlur() {
  // 滚动的时候
  if (document.activeElement?.tagName === 'INPUT') {
    if (document.activeElement.getAttribute('aria-expanded') === 'true') {
      (document.activeElement as HTMLElement)?.blur();
    }
  }
}

/**
 * @description 根据当前环境的协议判断采用何种写入剪贴板事件
 * @param copyText
 * @returns
 */
export function clipboardWriteText(copyText: string): Promise<{ message: boolean }> {
  // 判断存不存在clipboard并且是安全的协议
  if (navigator.clipboard && window.isSecureContext) {
    return new Promise((res, rej) => {
      navigator.clipboard
        .writeText(copyText)
        .then(() => {
          res({ message: true });
        })
        .catch(() => {
          rej({ message: false });
        });
    });
  } else {
    // 否则用被废弃的execCommand
    const textArea = document.createElement('textarea');
    textArea.value = copyText;
    // 使text area不在viewport，同时设置不可见
    textArea.style.position = 'absolute';
    textArea.style.opacity = '0';
    textArea.style.left = '-999999px';
    textArea.style.top = '-999999px';
    document.body.appendChild(textArea);
    textArea.focus();
    textArea.select();
    return new Promise((res, rej) => {
      // 执行复制命令并移除文本框
      if (document.execCommand('copy')) {
        document.execCommand('copy');
        res({ message: true });
      } else {
        rej({ message: false });
      }
      textArea.remove();
    });
  }
}

/**
 * @description: url path "///" 去重
 * @param {string} url
 * @return {string}
 */
export function resolveUrl(url: string) {
  if (url && typeof url === 'string') {
    const first = url.match('://');
    let firstText = '',
      lastText = url;
    if (first) {
      const i = first.index;
      firstText = url.substring(0, i + 3);
      lastText = url.substring(i + 2, url.length);
    }
    return firstText + lastText.replace(/\/{1,}/g, '/');
  }
  return url;
}

/**
 * @description: 统一处理ASSETS_URL拼接函数
 * @param {string} url
 * @return {*}
 */
export function getAssetsUrl(url: string) {
  if (url) {
    if (/http[s]{0,1}:\/\/([\w.]+\/?)\S*/.test(url)) {
      // 如果是http[s] 直接处理一下/即可
      return resolveUrl(url);
    }
    // 拼接后处理
    return resolveUrl(`${window.appConfig.ASSETS_URL}/${url}`);
  }
  return url;
}

export function isEasyvCache() {
  const params = parseSearchParams();
  const isEasyvCache = params.easyvCache == 'true';
  return isEasyvCache;
}

export function urlCacheReplace(url) {
  if (url && isShareScreenPage() && isEasyvCache()) {
    return url.replace('api', 'api/cache');
  }
  return url;
}


/**
 * @description: 价格转换
 * @param {number} price
 * @param {*} state take = 元转分；remove = 分转元
 * @return {*}
 */
export function priceConvert(price:number = 0,state:"take"|'remove' = 'take'){
  // 价格转换
  const price_num = typeof price === 'string' ? Number(price) : price;
  if(price_num && typeof price_num === 'number'){
    if(state === 'take'){
      return Number((price_num * 100).toFixed(2))
    }else if(state === 'remove'){
      return Number((price_num / 100).toFixed(2))
    }
  }
  return price_num;
}