import { IPlatformComponent, OFFICIAL_LIB_KEY } from '@cloudbase/cals';
import { app as _app, IAppAPI } from '../app/global-api';
import { auth } from '@cloudbase/weda-client';
import { inWxWork } from './sso/helper';
import { tryWecomLogin } from './sso/init';
import { isEqual as lodashIsEqual } from 'lodash-es';
import { IComponentInfo, IDataFrom } from './adapter/generate';

const { loginScope, getAccessToken } = auth;

/**
 * All data bindings are generated as functions: (forItems) => any
 * @param {*} dataBinds
 * @param {*} forItems
 */
export function resolveDataBinds(
  dataBinds,
  forItems,
  codeContext: { $WEAPPS_COMP?: any; event?: any },
  scopeContext,
  throwError,
  dataContext,
  $w,
  paramsContext,
) {
  const resolvedProps = {};
  for (const prop in dataBinds) {
    let fn = dataBinds[prop];
    try {
      if (codeContext?.$WEAPPS_COMP) {
        fn = fn.bind(codeContext.$WEAPPS_COMP);
      }
      resolvedProps[prop] = fn(forItems, codeContext?.event, dataContext, $w, scopeContext, paramsContext);
    } catch (e) {
      console.error('Error resolving data binding', prop, dataBinds[prop], e);
      if (throwError) {
        throw e;
      }
    }
  }
  return resolvedProps;
}

/**
 * 对函数进行批量绑定
 * @param {*} funcObj
 * @param {*} context
 */
export function bindFuncObj(funcObj = {}, context) {
  return Object.keys(funcObj).reduce((obj, fnName) => {
    obj[fnName] = funcObj[fnName]?.bind?.(context);
    return obj;
  }, {});
}

export function kebabCase(str) {
  return str.replace(/[A-Z]/g, (match) => {
    return `-${match.toLowerCase()}`;
  });
}

export function camelcase(str, firstUpperCase = false) {
  str = str.replace(/[_-]([a-z])/g, (l) => {
    return l[1].toUpperCase();
  });

  if (firstUpperCase) str = str.charAt(0).toUpperCase() + str.slice(1);

  return str;
}

export const isEmptyObj = (obj) => {
  if (!isPlainObject(obj)) {
    return false;
  }
  for (const i in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, i)) {
      return false;
    }
  }
  return true;
};

export function isPlainObject(src) {
  return Object.prototype.toString.call(src) === '[object Object]';
}

/**
 * @deprecated
 * 历史静态配置，不再新增
 */
const SCOPE_SLOT_COMPONENT_LIB = ['tea_basis', 'tea_shop', 'crm_basis'];

/**
 * @deprecated
 * 历史静态配置，不再新增
 */
const SCOPE_SLOT_MAP = SCOPE_SLOT_COMPONENT_LIB.reduce((map, lib) => {
  map[`${lib}:TableMatching`] = {
    headerSlot: true,
    recordSlot: true,
  };

  map[`${lib}:TableExpanded`] = {
    headerSlot: true,
    recordSlot: true,
  };

  map[`${lib}:TabsTable`] = {
    headerSlot: true,
    recordSlot: true,
  };

  map[`${lib}:Table`] = {
    headerSlot: true,
    recordSlot: true,
  };

  return map;
}, {});

export function isSlot(comp: IPlatformComponent) {
  return !comp.module && !comp.component;
}

interface IScopeSlotMeta {
  isScopeSlot: boolean;
  config?: IDataFrom;
  useScopeWidgetData?: boolean;
}

/**
 * 判断是否为作用域插槽
 * 1、根据组件dataForm是否有属性包含 x-slot-scope 属性
 * 2、保持历史穷举法不变
 */
export function getScopeSlotMeta(
  comp: {
    module: string;
    component: string;
  },
  slot: string,
  context?: { componentsInfoMap: Record<string, IComponentInfo> } | any,
): IScopeSlotMeta {
  const sourceKey = comp.module && comp.component && `${comp.module}:${comp.component}`;
  const map = SCOPE_SLOT_MAP[sourceKey];
  const res: IScopeSlotMeta = {
    isScopeSlot: map?.[slot] || false,
    useScopeWidgetData: false,
    config: undefined,
  };

  const info: IComponentInfo = context?.componentsInfoMap?.[sourceKey];
  function updateScopeSlotMeta(prop, config: IDataFrom) {
    res.isScopeSlot = true;
    res.useScopeWidgetData = true;
    res.config = { ...config };

    if (sourceKey === 'CLOUDBASE_BUSSINESS:ModelTable') {
      res.useScopeWidgetData = false;
    } else if (comp.module === OFFICIAL_LIB_KEY || comp.module === 'weda') {
      let gt = false;
      const properties: { records: any } = (info.properties as any)?.properties || info.properties;
      if (comp.component === 'WdTable' && properties.records) {
        gt = true;
      }

      if (!gt) {
        res.useScopeWidgetData = false;
      }
    }

    /**
     * 首先 版本号确定使用新版
     * 其次 若无 for 配置 默认补足配置
     */
    if (res.useScopeWidgetData) {
      if (
        (sourceKey === `${OFFICIAL_LIB_KEY}:WdTable` || sourceKey === `weda:WdTable`) &&
        (prop === 'cell' || prop === 'cell__custom__option') &&
        !res.config['x-slot-directives']
      ) {
        res.config['x-slot-directives'] = {
          for: 'records',
          /**
           * 没想好是记录 key 标识，用 find 去找
           * 还是直接用 index 来
           */
          _index: 'recordIndex',
        };
      }
    }
  }

  try {
    const { dataForm } = info || {};
    if (typeof dataForm === 'object') {
      /**
       * 优先精确匹配
       * 然后模糊匹配
       */
      if (dataForm[slot]?.['x-slot-scope']) {
        updateScopeSlotMeta(slot, dataForm[slot]);
      } else {
        // eslint-disable-next-line no-restricted-syntax
        for (const prop in dataForm) {
          if (slot.startsWith(`${prop}_`) && dataForm[prop]?.['x-slot-scope']) {
            updateScopeSlotMeta(prop, dataForm[prop]);
            break;
          }
        }
      }
    }
  } catch (e) {}

  return res;
}

export function findLoginPage(app = _app) {
  const { pages = [] } = app.__internal__.getConfig();
  return pages.find((item) => item.type === 'login');
}

/**
 * JSON.parse封装
 * @param {*} data
 * @param {*} defaultValue 失败时返回到默认值
 * @returns
 */
const jsonParse = (data: string, defaultValue?: unknown) => {
  try {
    return JSON.parse(data);
  } catch (err) {
    return defaultValue;
  }
};

/**
 * 缓存用户信息，非首次进入页面优先使用缓存
 * @param param0
 * @returns
 */
export const dealCache = async ({
  key,
  cacheKey,
  value,
  type = 'get',
}: {
  key: 'AUTH_CONFIG' | 'RENDER_ACCESS' | string; // localstorage的key
  cacheKey?: string; // 缓存对象拼接key
  value?: unknown; // 缓存值
  type: 'get' | 'set' | 'remove'; // 缓存操作类型
}) => {
  if (!localStorage || !key) return undefined;
  try {
    const { clientId = '', envId = '', id } = _app.__internal__.getConfig();
    cacheKey = `${envId}-${clientId}_${cacheKey || id}`;
    // 缓存值为对象，保证针对各环境各用户的信息唯一，{ [cacheKey]: { [userKey]: value } }
    await _app.cloud.getCloudInstance();
    const { accessToken = '' } = (await getAccessToken()) || {};
    const userKey = `${accessToken}`;
    const cache = jsonParse(localStorage.getItem(key), {});

    if (type === 'get') {
      return cache[cacheKey]?.[userKey];
    } else if (type === 'set') {
      localStorage.setItem(key, JSON.stringify({ ...cache, [cacheKey]: { [userKey]: value } }));
    } else if (type === 'remove') {
      Object.keys(cache).forEach((key) => {
        if (key.startsWith(cacheKey)) {
          delete cache[key];
        }
      });

      localStorage.setItem(key, JSON.stringify(cache));
    }
  } catch (error) {
    return undefined;
  }
};

const getAuthConfigRes = async (app) => {
  try {
    const res = await app.cloud.callWedaApi({
      action: 'DescribeRuntimeResourceStrategy',
      data: {
        ResourceType: 'app',
        ResourceId: app.id,
      },
    });
    const settingData = {};
    // 云api不支持map只能传字符串，需要转换
    res?.forEach((item) => {
      settingData[item.Key] = ['AllowRegister', 'NeedLogin'].includes(item.Key) ? item.Value === '1' : item.Value;
    });
    dealCache({ key: 'AUTH_CONFIG', cacheKey: app.id, value: settingData, type: 'set' });
    return settingData;
  } catch (e) {
    return {
      NeedLogin: false,
      RejectStrategy: 'show_warning',
    };
  }
};

export async function getAuthConfig(app = _app) {
  const localCache = await dealCache({ key: 'AUTH_CONFIG', cacheKey: (app as any).id, type: 'get' });
  if (localCache) {
    // 优先使用缓存，后更新缓存
    setTimeout(() => {
      getAuthConfigRes(app);
    }, 0);

    return localCache;
  }

  return await getAuthConfigRes(app);
}

const getAccessPermissionRes = async ({ app, cacheKey, appId }) => {
  const res = await app.cloud.callWedaApi({
    action: 'DescribeResourcesPermission',
    data: {
      ResourceType: `app`,
      ResourceIdList: [cacheKey],
      AppResourceId: appId,
    },
  });
  if (Array.isArray(res)) {
    const resourceInfo = res.find((i) => i.ResourceId === cacheKey);
    const value = {
      isAccess: !!resourceInfo?.IsAccess,
      roleId: resourceInfo?.RoleId,
    };
    dealCache({ key: 'RENDER_ACCESS', cacheKey, value, type: 'set' });
    return value;
  } else {
    throw new Error(`鉴权接口返回参数不正确 - ${JSON.stringify(res)}`);
  }
};

async function getAccessPermission(app, appId, pageId) {
  const cacheKey = `${appId}-${pageId}`;
  const localCache = await dealCache({ key: 'RENDER_ACCESS', cacheKey, type: 'get' });

  if (localCache !== undefined) {
    // 延迟获取接口数据，让其他任务先执行
    setTimeout(() => {
      getAccessPermissionRes({ app, cacheKey, appId });
    }, 0);

    return localCache;
  }

  return await getAccessPermissionRes({ app, cacheKey, appId });
}

export async function checkAnonymous() {
  // 用户是否非匿名登录前端判断条件：有accessToken且scope不为anonymous
  // 此分支逻辑本不应该前端判断是否登录，历史原因后端短期内搞不定，后续后端优化后删除
  let isAnonymous = true;
  try {
    const [scope, { accessToken }] = await Promise.all([loginScope(), getAccessToken()]);
    if (accessToken && scope !== 'anonymous') {
      isAnonymous = false;
    }
  } catch (e) {}
  return isAnonymous;
}

export function formatErrorMsg(e) {
  let msg = e?.error_description || e?.message;
  const uri = e?.error_uri;
  msg = `${msg}${uri ? `-${uri}` : ''}`;
  if (msg.startsWith('Failed to fetch')) {
    msg = `网络故障, 请检查本地网络连接是否正常(${msg})`;
  }
  return msg;
}

/**
 * 检查页面权限
 **/
export async function checkAuth(app, appId, $page, generateLoginUrl?: GenerateLoginUrlType) {
  app.showNavigationBarLoading();
  const { loginConfigVersion } = app.__internal__.getConfig();
  const requestList = [getAccessPermission(app, appId, $page.id)];
  // 有登录配置版本号时，才拉取自定义登录配置进行校验
  if (loginConfigVersion) {
    requestList.push(getAuthConfig(app));
    requestList.push(checkAnonymous());
  }
  try {
    const [accessData, authConfig, isAnonymous] = await Promise.all(requestList);
    app.hideNavigationBarLoading();

    if (loginConfigVersion && isAnonymous) {
      if (authConfig.NeedLogin || (!accessData?.isAccess && authConfig.RejectStrategy === 'to_login')) {
        redirectToLogin(generateLoginUrl);
        return false;
      }
    }

    if (!accessData?.isAccess) {
      app.showToast({
        title: ['-2', '-4'].includes(accessData?.roleId) ? '默认访客无权限' : '页面无访问权限',
        icon: 'error',
      });
    }
    return accessData?.isAccess;
  } catch (e) {
    app.showModal({
      title: '页面鉴权失败',
      content: formatErrorMsg(e),
      showCancel: false,
    });
    // @ts-ignore
    window._aegis?.error?.('CUSTOM_ERROR:', e);
    return false;
  }
}

export type GenerateLoginUrlType = (ctx: { app: IAppAPI }, originFn: () => string) => string;

export const defaultGenerateLoginUrl: GenerateLoginUrlType = () => {
  return getLoginUrl();
};

function getLoginUrl() {
  const app = _app;
  const { envId, id, clientId, loginConfigVersion } = app.__internal__.getConfig();
  return `${location.origin}/__auth/?redirect_uri=${encodeURIComponent(
    location.href,
  )}&app_id=${id}&env_id=${envId}&client_id=${clientId}&config_version=${loginConfigVersion}`;
}

export async function redirectToLogin(generateLoginUrl: GenerateLoginUrlType) {
  const app = _app;

  const { loginConfigVersion, id } = app.__internal__.getConfig();
  // 去登录则清空权限缓存。
  dealCache({ key: 'RENDER_ACCESS', cacheKey: id, type: 'remove' });
  // 有登录配置版本号时，自定义登录开启，则跳转到自定义登录页面
  if (loginConfigVersion) {
    const url = generateLoginUrl({ app }, getLoginUrl);
    const alternative = function alternative() {
      app.redirectTo({
        mode: 'web',
        params: {},
        url,
      });
    };
    if (ENABLE_WECOM) {
      tryWecomLogin(new URL(url), alternative);
    } else {
      alternative();
    }
  } else {
    app.showToast({
      title: '用户未登录',
      icon: 'error',
    });
  }
}

export function generateDataContext(widget) {
  const dataContext = {};
  while (widget?._scope) {
    const current = widget;
    // 此处采用代理的方式，是为了可以获取到最新的 _scope.dataContext 防止 dataContext 引用被重新赋值
    Object.defineProperty(dataContext, current._scope.id, {
      get() {
        return current._scope.dataContext;
      },
    });
    widget = widget.parent;
  }
  return dataContext;
}

export let ENABLE_OBSERVER_REF = false;
export function setEnableObserverRef(value) {
  ENABLE_OBSERVER_REF = value;
}

export let ENABLE_PAGE_ROOT_ID = false;
export function setEnablePageRoot(value) {
  ENABLE_PAGE_ROOT_ID = value;
}
export let ENABLE_WECOM = false;
export function setEnableWecom(value) {
  ENABLE_WECOM = value;
}

/**
 * 是否开启原地企微登录
 * 1. 针对单环境白名单
 * 2. 在企微环境打开
 * 3. 自定义应用（即：非企业工作台)
 */
const WECOM_ENVID_LIST = [
  'lowcode-4gzdsu5k66946492',
  'lowcode-2gay8jgh25c7b1cf',
  'lowcode-4g9b78bx94404d85',
  'lowcode-8gmx7bat3135e2de',
];
export function isEnableWecom(envId) {
  const adminPortal = simpleCheckAdminPortal();
  if (inWxWork() && !adminPortal && WECOM_ENVID_LIST.includes(envId)) {
    return true;
  }
}

export function serde(a) {
  return JSON.parse(JSON.stringify(a));
}

const isObj = (a) => typeof a === 'object' && a !== null;

export function isEqual(a, b, shouldSerde = false) {
  if (shouldSerde && isObj(a) && isObj(b)) {
    // for Proxy created by mbox
    return lodashIsEqual(serde(a), serde(b));
  }
  return lodashIsEqual(a, b);
}

const _REPORTED: any = {};
export function reportTime(tag, time, only = false) {
  const excludes = ['app-nvzcvt10', 'app-msa2ihs9'];
  const wedahostconfig: any = window._WedaHostConfig;
  if (!window._aegis || !tag || simpleCheckAdminPortal() || !window._aegis_inited || excludes.includes(_app.id)) {
    return;
  }
  if (wedahostconfig && !wedahostconfig._REPORTED) {
    wedahostconfig._REPORTED = _REPORTED;
  }

  const CACHE_MAP = wedahostconfig?._REPORTED || _REPORTED;

  if (only && CACHE_MAP[tag]) {
    return;
  }
  CACHE_MAP[tag] = true;

  const { envVersion } = _app.__internal__?.getConfig?.() || {};

  try {
    let t = time === undefined ? performance?.now?.() || Date.now() - window._aegis_inited : time;
    window._aegis.reportTime({
      name: tag,
      duration: t,
      ext2: envVersion,
    });
    if (t > 5000 && performance?.getEntries) {
      window._aegis.report({
        msg: performance.getEntries(),
        ext2: envVersion,
        trace: 'trace',
      });
    }
  } catch (e) {}
}

export function reportEvent(tag) {
  const { envVersion } = _app.__internal__?.getConfig?.() || {};
  try {
    // console.log('>>>>>>reportevent:', tag);
    window._aegis?.reportEvent?.({
      name: tag,
      ext2: envVersion,
    });
  } catch (e) {}
}

export function commonReport(message) {
  const { envVersion } = _app.__internal__?.getConfig?.() || {};
  try {
    window._aegis?.report?.({
      msg: message,
      ext2: envVersion,
    });
  } catch (e) {}
}

export function simpleCheckAdminPortal() {
  return window._WedaHostConfig && window.__POWERED_BY_QIANKUN__;
}

export function getNetworkType(window) {
  const ua = window?.navigator?.userAgent || '';
  let networkTypeStr = ua.match(/NetType\/\w+/)
    ? ua
        .match(/NetType\/\w+/)?.[0]
        ?.toLowerCase()
        ?.replace('nettype/', '')
    : window?.navigator?.connection?.effectiveType?.toLowerCase() || '';
  const types = ['2g', '3g', '4g', '5g', 'wifi'];
  for (const type of types) {
    if (networkTypeStr.includes(type)) return type;
  }
  return networkTypeStr ? 'other' : '';
}
