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 && codeContext.$WEAPPS_COMP) {
        fn = fn.bind(codeContext.$WEAPPS_COMP);
      }
      resolvedProps[prop] = fn(
        forItems,
        codeContext && 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 { staticResourceAttribute } = props;
  staticResourceAttribute &&
    staticResourceAttribute.map((property) => {
      if (props.data && props.data[property]) {
        props.data[property] = getStaticResourceAttribute(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;
}

export function isScopeSlot(
  comp: {
    module: string;
    component: string;
  },
  slot
) {
  const sourceKey =
    comp.module && comp.component && `${comp.module}:${comp.component}`;
  const map = SCOPE_SLOT_MAP[sourceKey];
  return map && map[slot];
}

export function getStaticResourceAttribute(staticUrl) {
  if (/^\//.test(staticUrl)) {
    const { domain = '' } = runtime['app'] || {};
    const url = `https://${domain}${staticUrl}`;
    return url;
  }
  return staticUrl;
}

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) {
  const loginPage = findLoginPage(app);
  if (loginPage?.id === $page.id) {
    return true;
  }
  app.showNavigationBarLoading();
  const requestList = [getAccessPermission(app, appId, $page.id)];
  // 暂时先认为有登录页则自定义登录功能开启且生效
  if (loginPage) {
    requestList.push(getAuthConfig(app));
    requestList.push(checkAnonymous());
  }
  const [isAccess, authConfig, isAnonymous] = await Promise.all(requestList);
  app.hideNavigationBarLoading();

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

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

export function redirectToLogin(currentPage?) {
  // 去登录则清空权限缓存。
  _AUTH_CACHE_MAP = {};
  const app = _app;
  const loginPage = findLoginPage(app);
  if (!currentPage) {
    currentPage = app.utils.getCurrentPage() || {};
  }
  if (loginPage?.id === currentPage.id) {
    return true;
  }
  if (loginPage) {
    app.redirectTo({
      pageId: loginPage.id,
      params: {
        sourcePageId: currentPage.id,
        sourcePageParams: currentPage.params,
      },
    });
  } else {
    app.showToast({
      title: '用户未登录',
      icon: 'error',
    });
  }
}
