/**
 * Foliko Error Boundary - 错误边界系统
 * 捕获、处理和恢复错误，提供降级方案
 */

const { EventEmitter } = require('./event-emitter');
const { logger } = require('./logger');
const { safeErrorInfo, isErrorOfType } = require('./error');

/**
 * 错误严重级别
 */
const Severity = {
  LOW: 'low', // 可忽略的小问题
  MEDIUM: 'medium', // 需要关注但不阻断流程
  HIGH: 'high', // 阻断当前操作但可恢复
  CRITICAL: 'critical', // 致命错误，需要完全降级
};

/**
 * 错误恢复动作
 */
const RecoveryAction = {
  RETRY: 'retry', // 重试操作
  SKIP: 'skip', // 跳过当前步骤
  FALLBACK: 'fallback', // 使用降级方案
  ABORT: 'abort', // 中止流程
  IGNORE: 'ignore', // 忽略错误继续
};

/**
 * 错误上下文
 * @typedef {Object} ErrorContext
 * @property {string} operation - 操作名称
 * @property {string} component - 组件名称
 * @property {Object} metadata - 额外元数据
 * @property {number} attempt - 当前尝试次数
 */

/**
 * 错误边界配置
 * @typedef {Object} ErrorBoundaryConfig
 * @property {string} name - 边界名称
 * @property {Function} [onError] - 全局错误处理器
 * @property {Function} [onRecovery] - 恢复处理器
 * @property {Function} [fallback] - 降级函数
 * @property {boolean} [logErrors=true] - 是否记录错误
 * @property {boolean} [propagateErrors=false] - 是否向上传播错误
 */

/**
 * 错误边界类
 * 用于捕获和管理错误，提供恢复机制
 */
class ErrorBoundary extends EventEmitter {
  /**
   * @param {ErrorBoundaryConfig} config
   */
  constructor(config = {}) {
    super();

    this.name = config.name || 'AnonymousBoundary';
    this._onError = config.onError || null;
    this._onRecovery = config.onRecovery || null;
    this._fallback = config.fallback || null;
    this._logErrors = config.logErrors !== false;
    this._propagateErrors = config.propagateErrors || false;
    this._logger = logger.child('ErrorBoundary');

    // 错误统计
    this._errorCount = 0;
    this._lastError = null;
    this._errorHistory = [];
    this._maxHistorySize = config.maxHistorySize || 50;
  }

  /**
   * 执行可能抛出错误的操作
   * @param {Function} operation - 要执行的操作
   * @param {Object} [context] - 错误上下文
   * @returns {Promise<{success: boolean, result?: any, error?: Error, recovered?: boolean}>}
   */
  async execute(operation, context = {}) {
    const operationName = context.operation || 'anonymous';

    try {
      const result = await (typeof operation === 'function' ? operation() : operation);
      return { success: true, result };
    } catch (error) {
      this._errorCount++;
      this._lastError = error;

      // 记录到历史
      this._addToHistory(error, context);

      // 记录错误日志
      if (this._logErrors) {
        this._logError(error, context);
      }

      // 触发错误事件
      this.emit('error', {
        error,
        context,
        boundary: this.name,
      });

      // 调用全局错误处理器
      let recoveryAction = RecoveryAction.ABORT;
      if (this._onError) {
        try {
          // 使用 Promise.resolve 以支持 async 函数
          recoveryAction = await Promise.resolve(this._onError(error, context, this));
        } catch (handlerError) {
          this._logger.warn(`Error in onError handler: ${handlerError.message}`);
        }
      }

      // 根据恢复动作处理
      const { action, fallbackResult } = await this._handleRecovery(error, context, recoveryAction);

      // 触发恢复事件
      if (action !== RecoveryAction.ABORT) {
        this.emit('recovery', {
          error,
          context,
          action,
          boundary: this.name,
        });

        if (this._onRecovery) {
          try {
            await this._onRecovery(error, context, action, this);
          } catch (recoveryError) {
            log.warn(`Error in onRecovery handler: ${recoveryError.message}`);
          }
        }
      }

      // 如果需要传播错误，或者没有降级方案且要求传播，则抛出错误
      if (this._propagateErrors || (action === RecoveryAction.ABORT && !fallbackResult)) {
        throw error;
      }

      return {
        success: false,
        error,
        recovered: action !== RecoveryAction.ABORT,
        fallbackResult,
        action,
      };
    }
  }

  /**
   * 执行带重试的操作
   * @param {Function} operation - 要执行的操作
   * @param {Object} retryConfig - 重试配置
   * @param {Object} [context] - 错误上下文
   * @returns {Promise<any>}
   */
  async executeWithRetry(operation, retryConfig = {}, context = {}) {
    const { maxAttempts = 3, baseDelay = 1000, shouldRetry = () => true } = retryConfig;

    let lastError;

    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
      try {
        return await this.execute(operation, { ...context, attempt });
      } catch (error) {
        lastError = error;

        if (attempt >= maxAttempts || !shouldRetry(error, attempt)) {
          throw error;
        }

        // 计算延迟
        const delay = baseDelay * Math.pow(2, attempt - 1);
        log.debug(`Retry attempt ${attempt}/${maxAttempts} after ${delay}ms...`);

        await new Promise((resolve) => setTimeout(resolve, delay));
      }
    }

    throw lastError;
  }

  /**
   * 包装一个函数，使其错误被边界捕获
   * @param {Function} fn - 要包装的函数
   * @param {Object} [defaultContext] - 默认上下文
   * @returns {Function}
   */
  wrap(fn, defaultContext = {}) {
    return async (...args) => {
      const result = await this.execute(() => fn(...args), defaultContext);
      return result.success ? result.result : result.fallbackResult;
    };
  }

  /**
   * 处理错误恢复
   * @private
   */
  async _handleRecovery(error, context, recoveryAction) {
    let fallbackResult = null;
    let action = recoveryAction;

    switch (recoveryAction) {
      case RecoveryAction.FALLBACK:
        if (this._fallback) {
          try {
            fallbackResult = await this._fallback(error, context, this);
            action = RecoveryAction.RETRY; // fallback 成功后视为恢复
          } catch (fallbackError) {
            log.warn(`Fallback also failed: ${fallbackError.message}`);
            action = RecoveryAction.ABORT;
          }
        }
        break;

      case RecoveryAction.SKIP:
        log.debug(`Skipping operation: ${context.operation}`);
        fallbackResult = null;
        break;

      case RecoveryAction.IGNORE:
        log.debug(`Ignoring error: ${error.message}`);
        fallbackResult = undefined;
        break;

      default:
        action = RecoveryAction.ABORT;
    }

    return { action, fallbackResult };
  }

  /**
   * 记录错误
   * @private
   */
  _logError(error, context) {
    if (!this._logErrors) return;

    const errorInfo = safeErrorInfo(error);
    const severity = this._determineSeverity(error);

    const logMessage = `[${this.name}] Error in ${context.operation || 'unknown'}: ${error.message}`;

    switch (severity) {
      case Severity.LOW:
        this._logger.debug(logMessage);
        break;
      case Severity.MEDIUM:
        this._logger.warn(logMessage);
        break;
      case Severity.HIGH:
        this._logger.error(logMessage);
        break;
      case Severity.CRITICAL:
        this._logger.error(`CRITICAL: ${logMessage}`, { stack: error.stack });
        break;
    }
  }

  /**
   * 确定错误严重级别
   * @private
   */
  _determineSeverity(error) {
    if (isErrorOfType(error, require('./error').FolikoError)) {
      return error.context?.severity || Severity.MEDIUM;
    }

    // 根据错误类型和消息判断
    const message = (error.message || '').toLowerCase();

    if (message.includes('timeout') || message.includes('network')) {
      return Severity.MEDIUM;
    }

    if (message.includes('memory') || message.includes('fatal')) {
      return Severity.CRITICAL;
    }

    return Severity.HIGH;
  }

  /**
   * 添加错误到历史记录
   * @private
   */
  _addToHistory(error, context) {
    this._errorHistory.push({
      timestamp: new Date().toISOString(),
      error: safeErrorInfo(error),
      context,
    });

    // 保持历史记录大小
    if (this._errorHistory.length > this._maxHistorySize) {
      this._errorHistory.shift();
    }
  }

  /**
   * 获取错误统计
   * @returns {Object}
   */
  getStats() {
    return {
      totalErrors: this._errorCount,
      lastError: this._lastError ? safeErrorInfo(this._lastError) : null,
      historySize: this._errorHistory.length,
      recentErrors: this._errorHistory.slice(-10),
    };
  }

  /**
   * 清空错误历史
   */
  clearHistory() {
    this._errorHistory = [];
    this._lastError = null;
  }
}

/**
 * 创建错误边界实例的工厂函数
 * @param {ErrorBoundaryConfig} config
 * @returns {ErrorBoundary}
 */
function createErrorBoundary(config = {}) {
  return new ErrorBoundary(config);
}

/**
 * 组合多个错误边界
 * @param {ErrorBoundary[]} boundaries
 * @returns {ErrorBoundary}
 */
function combineBoundaries(boundaries) {
  const combined = new ErrorBoundary({
    name: 'CombinedBoundary',
  });

  // 将错误传播到所有子边界
  for (const boundary of boundaries) {
    boundary.on('error', (data) => combined.emit('error', data));
    boundary.on('recovery', (data) => combined.emit('recovery', data));
  }

  return combined;
}

module.exports = {
  ErrorBoundary,
  Severity,
  RecoveryAction,
  createErrorBoundary,
  combineBoundaries,
};
