/**
 * Error Recovery - Phase 8 Enterprise Pattern
 *
 * Handles crashes, recovers lost state, and provides
 * resilience against errors and edge cases.
 */

import store from '../stores/app-store.js';
import auditLogger from './audit-logger.js';
import persistence from './workflow-persistence.js';

class ErrorRecovery {
  constructor() {
    this.errorHandlers = new Map();
    this.recoveryPoints = [];
    this.maxRecoveryPoints = 5;
    this.setupGlobalErrorHandlers();
  }

  /**
   * Setup global error handlers
   */
  setupGlobalErrorHandlers() {
    // Unhandled promise rejections
    window.addEventListener('unhandledrejection', (event) => {
      this.handleError(event.reason, 'unhandled_promise');
      // Prevent default error handling
      event.preventDefault();
    });

    // Global errors
    window.addEventListener('error', (event) => {
      this.handleError(event.error, 'global_error');
    });

    // Log before unload (potential crash)
    window.addEventListener('beforeunload', () => {
      persistence.saveSnapshot();
      auditLogger.logAction('session_end', {
        cleanShutdown: true
      });
    });
  }

  /**
   * Register error handler for specific error type
   */
  registerHandler(errorType, handler) {
    this.handlers.set(errorType, handler);
  }

  /**
   * Create recovery point
   */
  createRecoveryPoint(label = '') {
    const point = {
      id: `recovery-${Date.now()}`,
      label,
      timestamp: new Date().toISOString(),
      snapshot: persistence.snapshot(),
      logs: auditLogger.getLogs({ limit: 50 }),
      state: store.get()
    };

    this.recoveryPoints.unshift(point);
    if (this.recoveryPoints.length > this.maxRecoveryPoints) {
      this.recoveryPoints.pop();
    }

    auditLogger.logAction('recovery_point_created', { label });
    return point.id;
  }

  /**
   * Get recovery points
   */
  getRecoveryPoints() {
    return this.recoveryPoints;
  }

  /**
   * Recover from recovery point
   */
  recover(pointId) {
    const point = this.recoveryPoints.find(p => p.id === pointId);
    if (!point) {
      auditLogger.logWarning('Recovery point not found', { pointId });
      return false;
    }

    try {
      // Restore workflow state
      persistence.restoreSnapshot(point.snapshot.id);
      auditLogger.logAction('recovered_from_point', {
        pointId,
        label: point.label
      });
      return true;
    } catch (e) {
      this.handleError(e, 'recovery_failed');
      return false;
    }
  }

  /**
   * Check if app is in crashed state
   */
  detectCrash() {
    const lastSnapshot = persistence.getLatestSnapshot();
    if (!lastSnapshot) return false;

    const lastActivityTime = new Date(lastSnapshot.timestamp);
    const now = new Date();
    const timeSinceLastActivity = now - lastActivityTime;

    // If no activity in last 5 minutes and session started, likely crashed
    return timeSinceLastActivity > 5 * 60 * 1000;
  }

  /**
   * Main error handler
   */
  handleError(error, context = 'unknown') {
    const errorId = auditLogger.logError(error, context);

    // Create recovery point before handling
    const recoveryId = this.createRecoveryPoint(`Error recovery: ${context}`);

    // Categorize error
    const category = this.categorizeError(error);

    // Apply recovery strategy
    const recovery = this.getRecoveryStrategy(category);
    if (recovery) {
      recovery.execute(error, store, auditLogger);
    }

    // Notify user
    this.notifyUser(error, category, errorId);

    return { errorId, recoveryId, category };
  }

  /**
   * Categorize error
   */
  categorizeError(error) {
    if (error.message.includes('Network')) return 'network';
    if (error.message.includes('timeout')) return 'timeout';
    if (error.message.includes('Permission')) return 'permission';
    if (error.message.includes('Authentication')) return 'auth';
    if (error.message.includes('not found')) return 'notfound';
    return 'unknown';
  }

  /**
   * Get recovery strategy for error category
   */
  getRecoveryStrategy(category) {
    const strategies = {
      network: {
        execute: (error, store, logger) => {
          store.notify('Network error - retrying...', 'warning');
          logger.logWarning('Network error detected', { retrying: true });
        },
        retryable: true
      },
      timeout: {
        execute: (error, store, logger) => {
          store.notify('Request timeout - please try again', 'warning');
          logger.logWarning('Request timeout', { timeout: true });
        },
        retryable: true
      },
      auth: {
        execute: (error, store, logger) => {
          store.notify('Authentication required - redirecting to login', 'error');
          window.location.hash = '#/login';
        },
        retryable: false
      },
      permission: {
        execute: (error, store, logger) => {
          store.notify('Access denied', 'error');
          logger.logWarning('Permission denied', { error: error.message });
        },
        retryable: false
      },
      notfound: {
        execute: (error, store, logger) => {
          store.notify('Resource not found', 'warning');
        },
        retryable: false
      }
    };

    return strategies[category] || strategies.unknown;
  }

  /**
   * Notify user of error
   */
  notifyUser(error, category, errorId) {
    const messages = {
      network: 'Network connection error. Please check your internet.',
      timeout: 'Request took too long. Please try again.',
      permission: 'You do not have permission to perform this action.',
      auth: 'Your session has expired. Please log in again.',
      notfound: 'The resource you requested could not be found.',
      unknown: 'An unexpected error occurred. Please try again.'
    };

    const message = messages[category] || messages.unknown;
    store.notify(`${message} (Error: ${errorId})`, 'error', 5000);
  }

  /**
   * Retry operation with exponential backoff
   */
  async retry(operation, maxRetries = 3, initialDelay = 1000) {
    for (let i = 0; i < maxRetries; i++) {
      try {
        return await operation();
      } catch (e) {
        if (i === maxRetries - 1) {
          throw e;
        }
        const delay = initialDelay * Math.pow(2, i);
        await new Promise(resolve => setTimeout(resolve, delay));
        auditLogger.logWarning('Retrying operation', { attempt: i + 1, maxRetries });
      }
    }
  }

  /**
   * Get crash report
   */
  getCrashReport() {
    const logs = auditLogger.getLogs();
    const errorLogs = logs.filter(l => l.level === 'error');

    return {
      timestamp: new Date().toISOString(),
      sessionId: auditLogger.sessionId,
      totalErrors: errorLogs.length,
      errors: errorLogs.slice(0, 10),
      recoveryPoints: this.recoveryPoints,
      lastSnapshot: persistence.getLatestSnapshot(),
      statistics: auditLogger.getStats()
    };
  }

  /**
   * Export crash report
   */
  exportCrashReport() {
    const report = this.getCrashReport();
    return JSON.stringify(report, null, 2);
  }
}

// Create and export singleton
const errorRecovery = new ErrorRecovery();

export { ErrorRecovery };
export default errorRecovery;
