import { invoke } from '@tauri-apps/api/core';

/**
 * 错误信息接口定义
 * 用于统一错误处理和日志记录的数据结构
 */
interface ErrorInfo {
  /** 错误类型，如: javascript_error, promise_rejection, vue_error, manual_error */
  type: 'javascript_error' | 'manual_error' | 'promise_rejection' | 'vue_error';
  /** 错误消息内容 */
  message: string;
  /** 错误堆栈信息（可选） */
  stack?: string;
  /** 发生错误的文件名（可选） */
  filename?: string;
  /** 发生错误的行号（可选） */
  lineno?: number;
  /** 发生错误的列号（可选） */
  colno?: number;
  /** 额外的错误信息（可选） */
  info?: string;
  /** 组件信息，用于标识错误来源组件（可选） */
  componentInfo?: string;
  /** 错误上下文信息（可选） */
  context?: string;
  /** 错误发生的时间戳，格式为 YYYY-MM-DD HH:MM:SS.sss */
  timestamp: string;
}

/**
 * 全局错误拦截和日志记录模块
 */
class ErrorHandler {
  /** 单例实例，确保整个应用只有一个错误处理器 */
  private static instance: ErrorHandler;
  /** 标识错误处理器是否已初始化，防止重复初始化 */
  private isInitialized = false;

  /**
   * 获取单例实例
   */
  public static getInstance(): ErrorHandler {
    if (!ErrorHandler.instance) {
      ErrorHandler.instance = new ErrorHandler();
    }
    return ErrorHandler.instance;
  }

  /**
   * 初始化错误拦截器
   */
  public init(): void {
    if (this.isInitialized) {
      console.warn('错误处理程序已初始化');
      return;
    }

    // 捕获 JavaScript 运行时错误
    window.addEventListener('error', (event) => {
      console.error('全局错误处理程序(error):', event);
      this.handleError({
        type: 'javascript_error',
        message: event.message,
        stack: event.error?.stack || '',
        filename: event.filename,
        lineno: event.lineno,
        colno: event.colno,
        timestamp: getData(),
      });
    });

    // 捕获 Promise 拒绝错误
    window.addEventListener('unhandledrejection', (event) => {
      console.error('全局错误处理程序(unhandledrejection):', event);
      /** 错误消息内容 */
      let message = '';
      /** 错误堆栈信息 */
      let stack = '';

      if (event.reason instanceof Error) {
        message = event.reason.message;
        stack = event.reason.stack || '';
      } else {
        message = String(event.reason);
      }
      this.handleError({ type: 'promise_rejection', message, stack, timestamp: getData() });
    });

    // 捕获 Vue 特定错误
    if (typeof window !== 'undefined' && window && (window as any).Vue) {
      (window as any).Vue.config.errorHandler = (err: Error, vm: any, info: string) => {
        console.error('全局错误处理程序(vue-error):', err);
        this.handleError({
          type: 'vue_error',
          message: err.message,
          stack: err.stack || '',
          info,
          componentInfo: vm?.$options?.name || 'Unknown Component',
          timestamp: getData(),
        });
      };
    }

    this.isInitialized = true;
  }

  /**
   * 手动捕获错误
   */
  public captureError(error: any, context?: any): void {
    /** 错误消息内容 */
    let message = '';
    /** 错误堆栈信息 */
    let stack = '';
    /** 错误类型标识 */
    const type = 'manual_error';

    if (error instanceof Error) {
      message = error.message;
      stack = error.stack || '';
    } else if (typeof error === 'string') {
      message = error;
    } else {
      message = JSON.stringify(error);
    }

    this.handleError({
      type,
      message,
      stack,
      context: context ? JSON.stringify(context) : undefined,
      timestamp: getData(),
    });
  }

  /**
   * 记录错误到日志文件
   */
  private async handleError(errorInfo: ErrorInfo): Promise<void> {
    try {
      // 构建错误消息
      const errorMessage = [
        `[${errorInfo.type}] ${errorInfo.message}`,
        errorInfo.filename ? `File: ${errorInfo.filename}:${errorInfo.lineno}:${errorInfo.colno}` : '',
        errorInfo.stack ? `Stack: ${errorInfo.stack}` : '',
        errorInfo.componentInfo ? `Component: ${errorInfo.componentInfo}` : '',
        errorInfo.context ? `Context: ${errorInfo.context}` : '',
      ]
        .filter(Boolean)
        .join(' | ');

      // 调用后端 Rust 函数记录前端错误到日志文件
      await invoke('log_from_frontend', {
        level: 'error',
        message: errorMessage,
      });
    } catch (logError) {
      // 如果记录错误失败，至少在控制台输出
      console.error('无法将错误记录到文件:', logError);
    }
  }
}

/**
 * 获取当前时间 YYYY-MM-DD 14:49:07
 */
function getData() {
  return new Date().toLocaleString('sv-SE', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
  });
}

export default ErrorHandler;
