/**

 * Used to configure the global error handling function, which can monitor vue errors, script errors, static resource errors and Promise errors

 */



import type { ErrorLogInfo } from '/#/store';



import { useErrorLogStoreWithOut } from '/@/store/modules/errorLog';



import { ErrorTypeEnum } from '/@/enums/exceptionEnum';

import { App } from 'vue';

import projectSetting from '/@/settings/projectSetting';



/**

 * Handling error stack information

 * @param error

 */

function processStackMsg(error: Error) {

  if (!error.stack) {

    return '';

  }

  let stack = error.stack

    .replace(/\n/gi, '') // Remove line breaks to save the size of the transmitted content

    .replace(/\bat\b/gi, '@') // At in chrome, @ in ff

    .split('@') // Split information with @

    .slice(0, 9) // The maximum stack length (Error.stackTraceLimit = 10), so only take the first 10

    .map((v) => v.replace(/^\s*|\s*$/g, '')) // Remove extra spaces

    .join('~') // Manually add separators for later display

    .replace(/\?[^:]+/gi, ''); // Remove redundant parameters of js file links (?x=1 and the like)

  const msg = error.toString();

  if (stack.indexOf(msg) < 0) {

    stack = msg + '@' + stack;

  }

  return stack;

}



/**

 * get comp name

 * @param vm

 */

function formatComponentName(vm: any) {

  if (vm.$root === vm) {

    return {

      name: 'root',

      path: 'root',

    };

  }



  const options = vm.$options as any;

  if (!options) {

    return {

      name: 'anonymous',

      path: 'anonymous',

    };

  }

  const name = options.name || options._componentTag;

  return {

    name: name,

    path: options.__file,

  };

}



/**

 * Configure Vue error handling function

 */



function vueErrorHandler(err: Error, vm: any, info: string) {

  const errorLogStore = useErrorLogStoreWithOut();

  const { name, path } = formatComponentName(vm);

  errorLogStore.addErrorLogInfo({

    type: ErrorTypeEnum.VUE,

    name,

    file: path,

    message: err.message,

    stack: processStackMsg(err),

    detail: info,

    url: window.location.href,

  });

}



/**

 * Configure script error handling function

 */

export function scriptErrorHandler(event: Event | string, source?: string, lineno?: number, colno?: number, error?: Error) {

  if (event === 'Script error.' && !source) {

    return false;

  }

  const errorInfo: Partial<ErrorLogInfo> = {};

  colno = colno || (window.event && (window.event as any).errorCharacter) || 0;

  errorInfo.message = event as string;

  if (error?.stack) {

    errorInfo.stack = error.stack;

  } else {

    errorInfo.stack = '';

  }

  const name = source ? source.substr(source.lastIndexOf('/') + 1) : 'script';

  const errorLogStore = useErrorLogStoreWithOut();

  errorLogStore.addErrorLogInfo({

    type: ErrorTypeEnum.SCRIPT,

    name: name,

    file: source as string,

    detail: 'lineno' + lineno,

    url: window.location.href,

    ...(errorInfo as Pick<ErrorLogInfo, 'message' | 'stack'>),

  });

  return true;

}



/**

 * Configure Promise error handling function

 */

function registerPromiseErrorHandler() {

  window.addEventListener(

    'unhandledrejection',

    function (event) {

      const errorLogStore = useErrorLogStoreWithOut();

      errorLogStore.addErrorLogInfo({

        type: ErrorTypeEnum.PROMISE,

        name: 'Promise Error!',

        file: 'none',

        detail: 'promise error!',

        url: window.location.href,

        stack: 'promise error!',

        message: event.reason,

      });

    },

    true

  );

}



/**

 * Configure monitoring resource loading error handling function

 */

function registerResourceErrorHandler() {

  // Monitoring resource loading error(img,script,css,and jsonp)

  window.addEventListener(

    'error',

    function (e: Event) {

      const target = e.target ? e.target : (e.srcElement as any);

      const errorLogStore = useErrorLogStoreWithOut();

      errorLogStore.addErrorLogInfo({

        type: ErrorTypeEnum.RESOURCE,

        name: 'Resource Error!',

        file: (e.target || ({} as any)).currentSrc,

        detail: JSON.stringify({

          tagName: target.localName,

          html: target.outerHTML,

          type: e.type,

        }),

        url: window.location.href,

        stack: 'resource is not found',

        message: (e.target || ({} as any)).localName + ' is load error',

      });

    },

    true

  );

}



/**

 * Configure global error handling

 * @param app

 */

export function setupErrorHandle(app: App) {

  const { useErrorHandle } = projectSetting;

  if (!useErrorHandle) {

    return;

  }

  // Vue exception monitoring;

  app.config.errorHandler = vueErrorHandler;



  // script error

  window.onerror = scriptErrorHandler;



  //  promise exception

  registerPromiseErrorHandler();



  // Static resource exception

  registerResourceErrorHandler();

}