import { IPlatformApp, IPlatformComponent } from '@cloudbase/cals';
import { widgetKeys } from './widgets';
import { runtime } from './adapter';
import { app as _app } from '../app/global-api';
import { auth } from '@cloudbase/weda-client';
const { loginScope, getAccessToken } = auth;

/**
 * All data bindings are generated as functions: (forItems) => any
 * @param {*} dataBinds
 * @param {*} forItems
 */
export function resolveDataBinds(dataBinds, forItems, codeContext, scopeContext, throwError, dataContext) {
  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);
    } 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, function (match) {
    return `-${match.toLowerCase()}`;
  });
}

export function camelcase(str, firstUpperCase = false) {
  str = str.replace(/[_-]([a-z])/g, function (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]';
}

/**
 * 用于处理自定义组件props传参结构，对系统变量进行保留
 */
export function resolveComponentProps(props, isPlainProps) {
  const { __internal__ = {} } = _app || {};
  const { resolveStaticResourceUrl } = __internal__ || ({} as any);
  const { staticResourceAttribute } = props;
  staticResourceAttribute?.map?.((property) => {
    if (props.data?.[property]) {
      props.data[property] = resolveStaticResourceUrl(props.data[property]);
    }
  });
  if (!isPlainProps) {
    return {
      ...props,
    };
  }
  const { data = {}, events = [], ...restProps } = props;
  const customProps = { ...data };
  const builtinProps = [
    // react 保留字
    'ref',
    'key',
    'dangerouslySetInnerHTML',
    'className',
    'htmlFor',
    'style',
    'contentEditable',
    // lowcode 保留字
    'events',
    'children',
    '_parentId',
    '_visible',
    '_listeners',
    ...widgetKeys,
    'classList',
    'widgetType',
    'staticResourceAttribute',
    // 小程序保留字
    'id',
    'class',
    'hidden',
    'slot',
  ];
  // delete builtin props
  builtinProps.map((prop) => delete customProps[prop]);
  // 选区events处理
  const { _selectableBlockEvents, ...restData } = customProps;
  const { emit: _selectableBlockEmit } = _selectableBlockEvents;
  const selectableBlockEvents =
    _selectableBlockEvents?.events?.reduce((events, item) => {
      const propName = item;
      events[propName] = (e) => _selectableBlockEmit(propName, e);
      return events;
    }, {}) || {};

  try {
    _selectableBlockEvents.events = new Proxy(selectableBlockEvents, {
      get(obj, prop) {
        return prop in obj ? obj[prop] : (e) => _selectableBlockEmit(prop, e);
      },
    });
  } catch (e) {
    console.warn('selectableBlockEvents', selectableBlockEvents);
  }
  return {
    ...restData,
    ...restProps,
    _selectableBlockEvents,
    events: new Proxy(
      events.reduce((events, item) => {
        const propName = item;
        events[propName] = (e) => restProps.emit(propName, e);
        return events;
      }, {}),
      {
        get(obj, prop) {
          return prop in obj ? obj[prop] : (e) => restProps.emit(prop, e);
        },
      },
    ),
  };
}

const SCOPE_SLOT_COMPONENT_LIB = ['tea_basis', 'tea_shop', 'crm_basis'];

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;
}

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

  let hasXslotScope = false;
  try {
    const dataForm = context?.componentsInfoMap?.[sourceKey]?.dataForm;
    if (typeof dataForm === 'object') {
      hasXslotScope = Object.keys(dataForm).some(
        (prop) => slot.startsWith(`${prop}_`) && dataForm[prop]?.['x-slot-scope'],
      );
    }
  } catch (e) {}

  const map = SCOPE_SLOT_MAP[sourceKey];
  return hasXslotScope || (map && map[slot]);
}

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

let _AUTH_CONFIG_CACHE = null;
export async function getAuthConfig(app = _app) {
  if (_AUTH_CONFIG_CACHE) {
    return _AUTH_CONFIG_CACHE;
  }
  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;
    });
    _AUTH_CONFIG_CACHE = settingData;
    return _AUTH_CONFIG_CACHE;
  } catch (e) {
    return {
      NeedLogin: false,
      RejectStrategy: 'show_warning',
    };
  }
}

let _AUTH_CACHE_MAP = {};
async function getAccessPermission(app, appId, pageId) {
  const cacheKey = `${appId}-${pageId}`;
  if (_AUTH_CACHE_MAP[cacheKey] !== undefined) {
    return _AUTH_CACHE_MAP[cacheKey];
  }

  let isAccess = false;
  try {
    const res = await app.cloud.callWedaApi({
      action: 'DescribeResourcesPermission',
      data: {
        ResourceType: `app`,
        ResourceIdList: [cacheKey],
        AppResourceId: appId,
      },
    });
    if (Array.isArray(res) && res.length > 0) {
      isAccess = !!res[0].IsAccess;
    }
    _AUTH_CACHE_MAP[cacheKey] = isAccess;
  } catch (e) {
    console.warn('getAccessPermission', e);
  }
  return isAccess;
}

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 async function checkAuth(app, appId, $page) {
  app.showNavigationBarLoading();
  const { loginConfigVersion } = app.__internal__.getConfig();
  const requestList = [getAccessPermission(app, appId, $page.id)];
  // 有登录配置版本号时，才拉取自定义登录配置进行校验
  if (loginConfigVersion) {
    requestList.push(getAuthConfig(app));
    requestList.push(checkAnonymous());
  }
  const [isAccess, authConfig, isAnonymous] = await Promise.all(requestList);
  app.hideNavigationBarLoading();

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

  if (!isAccess) {
    app.showToast({
      title: '页面无访问权限',
      icon: 'error',
    });
  }
  return isAccess;
}

export async function redirectToLogin(currentPage?) {
  // 去登录则清空权限缓存。
  _AUTH_CACHE_MAP = {};
  const app = _app;

  const { staticResourceDomain, envId, id, clientId, loginConfigVersion } = app.__internal__.getConfig();
  // 有登录配置版本号时，自定义登录开启，则跳转到自定义登录页面
  if (loginConfigVersion) {
    // 实时预览需要传递staticResourceDomain，用于代理auth页面和登录配置，都需要做白名单处理
    const url = `${location.origin}/__auth/?redirect_uri=${encodeURIComponent(
      location.href,
    )}&app_id=${id}&env_id=${envId}&client_id=${clientId}&config_version=${loginConfigVersion}&s_domain=${staticResourceDomain}`;
    app.redirectTo({
      mode: 'web',
      params: {},
      url,
    });
  } else {
    app.showToast({
      title: '用户未登录',
      icon: 'error',
    });
  }
}
