import Taro from '@tarojs/taro';

// 定义本地的RequestError类，避免循环依赖
export class RequestError extends Error {
  public type: string;
  public code?: number;
  public data?: any;

  constructor(message: string, type: string, code?: number, data?: any) {
    super(message);
    this.name = 'RequestError';
    this.type = type;
    this.code = code;
    this.data = data;
  }
}

// 错误类型枚举
export enum ErrorType {
  NETWORK_ERROR = 'NETWORK_ERROR',
  TIMEOUT_ERROR = 'TIMEOUT_ERROR',
  BUSINESS_ERROR = 'BUSINESS_ERROR',
  AUTH_ERROR = 'AUTH_ERROR',
  UNKNOWN_ERROR = 'UNKNOWN_ERROR'
}

// 错误级别枚举
export enum ErrorLevel {
  INFO = 'info',
  WARNING = 'warning',
  ERROR = 'error',
  FATAL = 'fatal'
}

// 错误信息接口
export interface ErrorInfo {
  message: string;
  level: ErrorLevel;
  type: string;
  stack?: string;
  extra?: any;
  timestamp: number;
  userId?: string;
  page?: string;
}

/**
 * 全局错误处理器
 */
class GlobalErrorHandler {
  private static instance: GlobalErrorHandler;
  private errorQueue: ErrorInfo[] = [];
  private maxQueueSize = 50;

  private constructor() {
    this.setupGlobalErrorHandlers();
  }

  public static getInstance(): GlobalErrorHandler {
    if (!GlobalErrorHandler.instance) {
      GlobalErrorHandler.instance = new GlobalErrorHandler();
    }
    return GlobalErrorHandler.instance;
  }

  /**
   * 设置全局错误处理器
   */
  private setupGlobalErrorHandlers() {
    // 捕获未处理的Promise rejection
    if (typeof window !== 'undefined') {
      window.addEventListener('unhandledrejection', (event) => {
        this.handleError(event.reason, ErrorLevel.ERROR, 'UnhandledPromiseRejection');
        event.preventDefault();
      });
    }
  }

  /**
   * 处理错误
   */
  public handleError(
    error: any,
    level: ErrorLevel = ErrorLevel.ERROR,
    type: string = 'Unknown',
    extra?: any
  ) {
    const errorInfo = this.createErrorInfo(error, level, type, extra);

    // 添加到错误队列
    this.addToQueue(errorInfo);

    // 根据错误级别决定处理方式
    switch (level) {
      case ErrorLevel.FATAL:
        this.handleFatalError(errorInfo);
        break;
      case ErrorLevel.ERROR:
        this.handleNormalError(errorInfo);
        break;
      case ErrorLevel.WARNING:
        this.handleWarning(errorInfo);
        break;
      case ErrorLevel.INFO:
        this.handleInfo(errorInfo);
        break;
    }

    // 上报错误
    this.reportError(errorInfo);
  }

  /**
   * 创建错误信息对象
   */
  private createErrorInfo(
    error: any,
    level: ErrorLevel,
    type: string,
    extra?: any
  ): ErrorInfo {
    let message = '未知错误';
    let stack: string | undefined;

    if (error instanceof Error) {
      message = error.message;
      stack = error.stack;
    } else if (error instanceof RequestError) {
      message = error.message;
      type = error.type;
      extra = { ...extra, code: error.code, data: error.data };
    } else if (typeof error === 'string') {
      message = error;
    } else if (error && typeof error === 'object') {
      message = error.message || error.errMsg || JSON.stringify(error);
    }

    return {
      message,
      level,
      type,
      stack,
      extra,
      timestamp: Date.now(),
      userId: Taro.getStorageSync('UserId') || undefined,
      page: this.getCurrentPage()
    };
  }

  /**
   * 获取当前页面路径
   */
  private getCurrentPage(): string {
    try {
      const pages = Taro.getCurrentPages();
      const currentPage = pages[pages.length - 1];
      return currentPage?.route || 'unknown';
    } catch {
      return 'unknown';
    }
  }

  /**
   * 添加到错误队列
   */
  private addToQueue(errorInfo: ErrorInfo) {
    this.errorQueue.push(errorInfo);

    // 保持队列大小
    if (this.errorQueue.length > this.maxQueueSize) {
      this.errorQueue.shift();
    }
  }

  /**
   * 处理致命错误
   */
  private handleFatalError(errorInfo: ErrorInfo) {
    console.error('Fatal Error:', errorInfo);

    Taro.showModal({
      title: '严重错误',
      content: '应用遇到严重错误，需要重启',
      showCancel: false,
      confirmText: '重启应用',
      success: () => {
        Taro.reLaunch({ url: '/pages/index/index' });
      }
    });
  }

  /**
   * 处理普通错误
   */
  private handleNormalError(errorInfo: ErrorInfo) {
    console.error('Error:', errorInfo);

    // 根据错误类型显示不同的提示
    let title = '操作失败';

    if (errorInfo.type === ErrorType.NETWORK_ERROR) {
      title = '网络连接失败';
    } else if (errorInfo.type === ErrorType.TIMEOUT_ERROR) {
      title = '请求超时';
    } else if (errorInfo.type === ErrorType.AUTH_ERROR) {
      title = '认证失败';
    }

    Taro.showToast({
      title: errorInfo.message || title,
      icon: 'error',
      duration: 2000
    });
  }

  /**
   * 处理警告
   */
  private handleWarning(errorInfo: ErrorInfo) {
    console.warn('Warning:', errorInfo);

    // 警告通常不需要用户交互，只记录日志
  }

  /**
   * 处理信息
   */
  private handleInfo(errorInfo: ErrorInfo) {
    console.info('Info:', errorInfo);
  }

  /**
   * 上报错误到服务器
   */
  private reportError(errorInfo: ErrorInfo) {
    try {
      // 这里可以实现错误上报逻辑
      // 例如发送到后端日志系统、第三方监控服务等

      // 示例：发送到后端
      // request.post('/api/error/report', errorInfo).catch(() => {
      //   // 上报失败也不要影响用户体验
      // });

      // 开发环境下打印详细信息
      if (process.env.NODE_ENV === 'development') {
        console.group('🚨 Error Report');
        console.log('Message:', errorInfo.message);
        console.log('Level:', errorInfo.level);
        console.log('Type:', errorInfo.type);
        console.log('Page:', errorInfo.page);
        console.log('UserId:', errorInfo.userId);
        console.log('Timestamp:', new Date(errorInfo.timestamp).toLocaleString());
        if (errorInfo.stack) {
          console.log('Stack:', errorInfo.stack);
        }
        if (errorInfo.extra) {
          console.log('Extra:', errorInfo.extra);
        }
        console.groupEnd();
      }
    } catch (reportError) {
      console.error('Failed to report error:', reportError);
    }
  }

  /**
   * 获取错误队列
   */
  public getErrorQueue(): ErrorInfo[] {
    return [...this.errorQueue];
  }

  /**
   * 清空错误队列
   */
  public clearErrorQueue() {
    this.errorQueue = [];
  }
}

// 导出单例实例
export const errorHandler = GlobalErrorHandler.getInstance();

// 便捷方法
export const handleError = (error: any, level?: ErrorLevel, type?: string, extra?: any) => {
  errorHandler.handleError(error, level, type, extra);
};

export const handleFatalError = (error: any, extra?: any) => {
  errorHandler.handleError(error, ErrorLevel.FATAL, 'FatalError', extra);
};

export const handleWarning = (error: any, extra?: any) => {
  errorHandler.handleError(error, ErrorLevel.WARNING, 'Warning', extra);
};

export const handleInfo = (message: string, extra?: any) => {
  errorHandler.handleError(message, ErrorLevel.INFO, 'Info', extra);
};

export default errorHandler;
