/**
 * Enhanced Error Handling Utility
 * Implements MCP Design Guide Section 5.1 principles for error visibility and self-correction
 */

export interface DetailedError {
  message: string;
  code: string;
  category: 'validation' | 'execution' | 'external' | 'system';
  details: Record<string, any>;
  context?: Record<string, any>;
  recoverable: boolean;
  suggestedActions?: string[];
  originalError?: Error;
}

export interface RetryConfig {
  maxAttempts: number;
  baseDelay: number; // milliseconds
  maxDelay: number;
  backoffMultiplier: number;
  retryableErrors: string[];
}

export class MCPError extends Error {
  public readonly code: string;
  public readonly category: string;
  public readonly details: Record<string, any>;
  public readonly context?: Record<string, any>;
  public readonly recoverable: boolean;
  public readonly suggestedActions?: string[];
  public readonly originalError?: Error;

  constructor(error: DetailedError) {
    super(error.message);
    this.name = 'MCPError';
    this.code = error.code;
    this.category = error.category;
    this.details = error.details;
    this.context = error.context;
    this.recoverable = error.recoverable;
    this.suggestedActions = error.suggestedActions;
    this.originalError = error.originalError;
  }

  toMCPResponse() {
    return {
      content: [
        {
          type: 'text',
          text: this.formatErrorMessage(),
        },
      ],
      structured: {
        error: true,
        code: this.code,
        category: this.category,
        recoverable: this.recoverable,
        details: this.details,
        context: this.context,
        suggestedActions: this.suggestedActions,
      },
    };
  }

  private formatErrorMessage(): string {
    let message = `❌ **Error**: ${this.message}\n\n`;
    message += `🔍 **Code**: ${this.code}\n`;
    message += `📋 **Category**: ${this.category}\n`;
    
    if (Object.keys(this.details).length > 0) {
      message += `📝 **Details**:\n`;
      for (const [key, value] of Object.entries(this.details)) {
        message += `  • ${key}: ${JSON.stringify(value)}\n`;
      }
    }

    if (this.context && Object.keys(this.context).length > 0) {
      message += `🎯 **Context**:\n`;
      for (const [key, value] of Object.entries(this.context)) {
        message += `  • ${key}: ${JSON.stringify(value)}\n`;
      }
    }

    if (this.suggestedActions && this.suggestedActions.length > 0) {
      message += `\n💡 **Suggested Actions**:\n`;
      this.suggestedActions.forEach((action, index) => {
        message += `${index + 1}. ${action}\n`;
      });
    }

    if (this.recoverable) {
      message += `\n🔄 This error is recoverable. You can retry the operation.`;
    }

    return message;
  }
}

export class ErrorHandler {
  private static readonly DEFAULT_RETRY_CONFIG: RetryConfig = {
    maxAttempts: 3,
    baseDelay: 1000,
    maxDelay: 10000,
    backoffMultiplier: 2,
    retryableErrors: ['NETWORK_ERROR', 'TIMEOUT', 'RATE_LIMIT', 'TEMPORARY_UNAVAILABLE'],
  };

  /**
   * Wraps a function with comprehensive error handling and retry logic
   */
  static async withErrorHandling<T>(
    operation: () => Promise<T>,
    context: { tool: string; module: string; params?: any },
    retryConfig: Partial<RetryConfig> = {}
  ): Promise<T> {
    const config = { ...this.DEFAULT_RETRY_CONFIG, ...retryConfig };
    let lastError: Error | undefined;

    for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
      try {
        return await operation();
      } catch (error) {
        lastError = error as Error;
        const mcpError = this.transformError(error as Error, context);

        // Don't retry non-recoverable errors
        if (!mcpError.recoverable || attempt === config.maxAttempts) {
          throw mcpError;
        }

        // Don't retry errors that aren't in the retryable list
        if (!config.retryableErrors.includes(mcpError.code)) {
          throw mcpError;
        }

        // Calculate exponential backoff delay
        const delay = Math.min(
          config.baseDelay * Math.pow(config.backoffMultiplier, attempt - 1),
          config.maxDelay
        );

        console.error(`[${context.module}:${context.tool}] Attempt ${attempt} failed, retrying in ${delay}ms:`, mcpError.message);
        await this.sleep(delay);
      }
    }

    // This should never be reached, but TypeScript requires it
    throw this.transformError(lastError!, context);
  }

  /**
   * Transform generic errors into detailed MCP errors with full visibility
   */
  static transformError(error: Error, context: { tool: string; module: string; params?: any }): MCPError {
    // Network/HTTP errors
    if (error.message.includes('fetch') || error.message.includes('network')) {
      return new MCPError({
        message: `Network connection failed: ${error.message}`,
        code: 'NETWORK_ERROR',
        category: 'external',
        details: {
          originalMessage: error.message,
          stack: error.stack,
        },
        context,
        recoverable: true,
        suggestedActions: [
          'Check your internet connection',
          'Verify the service endpoint is accessible',
          'Try again in a few moments',
        ],
        originalError: error,
      });
    }

    // Validation errors
    if (error.message.includes('required') || error.message.includes('invalid')) {
      return new MCPError({
        message: `Input validation failed: ${error.message}`,
        code: 'VALIDATION_ERROR',
        category: 'validation',
        details: {
          originalMessage: error.message,
          providedParams: context.params,
        },
        context,
        recoverable: true,
        suggestedActions: [
          'Check all required parameters are provided',
          'Verify parameter types match the schema',
          'Review the tool documentation for correct usage',
        ],
        originalError: error,
      });
    }

    // File system errors
    if (error.message.includes('ENOENT') || error.message.includes('EACCES')) {
      return new MCPError({
        message: `File system operation failed: ${error.message}`,
        code: 'FILE_SYSTEM_ERROR',
        category: 'system',
        details: {
          originalMessage: error.message,
          operation: context.tool,
        },
        context,
        recoverable: false,
        suggestedActions: [
          'Check file paths are correct and accessible',
          'Verify you have necessary permissions',
          'Ensure the target directory exists',
        ],
        originalError: error,
      });
    }

    // Rate limiting
    if (error.message.includes('rate limit') || error.message.includes('429')) {
      return new MCPError({
        message: `Rate limit exceeded: ${error.message}`,
        code: 'RATE_LIMIT',
        category: 'external',
        details: {
          originalMessage: error.message,
        },
        context,
        recoverable: true,
        suggestedActions: [
          'Wait before retrying the operation',
          'Reduce the frequency of requests',
          'Check API quota and limits',
        ],
        originalError: error,
      });
    }

    // Authentication errors
    if (error.message.includes('unauthorized') || error.message.includes('401')) {
      return new MCPError({
        message: `Authentication failed: ${error.message}`,
        code: 'AUTH_ERROR',
        category: 'external',
        details: {
          originalMessage: error.message,
        },
        context,
        recoverable: false,
        suggestedActions: [
          'Check your credentials are correct',
          'Verify API keys or tokens are valid',
          'Ensure you have necessary permissions',
        ],
        originalError: error,
      });
    }

    // Generic error fallback
    return new MCPError({
      message: `Unexpected error in ${context.module}.${context.tool}: ${error.message}`,
      code: 'UNKNOWN_ERROR',
      category: 'system',
      details: {
        originalMessage: error.message,
        stack: error.stack,
        errorType: error.constructor.name,
      },
      context,
      recoverable: false,
      suggestedActions: [
        'Review the error details for specific issues',
        'Check system logs for additional information',
        'Report this error if it persists',
      ],
      originalError: error,
    });
  }

  /**
   * Create a validation error for schema violations
   */
  static createValidationError(
    field: string,
    value: any,
    constraint: string,
    context: { tool: string; module: string }
  ): MCPError {
    return new MCPError({
      message: `Parameter '${field}' violates constraint: ${constraint}`,
      code: 'PARAMETER_VALIDATION_ERROR',
      category: 'validation',
      details: {
        field,
        value,
        constraint,
        valueType: typeof value,
      },
      context,
      recoverable: true,
      suggestedActions: [
        `Ensure '${field}' meets the requirement: ${constraint}`,
        'Check the tool documentation for valid parameter formats',
        'Verify the parameter type matches expectations',
      ],
    });
  }

  /**
   * Create a resource not found error
   */
  static createNotFoundError(
    resourceType: string,
    identifier: string,
    context: { tool: string; module: string }
  ): MCPError {
    return new MCPError({
      message: `${resourceType} not found: ${identifier}`,
      code: 'RESOURCE_NOT_FOUND',
      category: 'validation',
      details: {
        resourceType,
        identifier,
      },
      context,
      recoverable: false,
      suggestedActions: [
        `Verify the ${resourceType} ID '${identifier}' exists`,
        `Use list commands to find available ${resourceType}s`,
        'Check for typos in the identifier',
      ],
    });
  }

  /**
   * Create a dependency error for missing prerequisites
   */
  static createDependencyError(
    dependency: string,
    context: { tool: string; module: string }
  ): MCPError {
    return new MCPError({
      message: `Missing dependency: ${dependency}`,
      code: 'DEPENDENCY_ERROR',
      category: 'validation',
      details: {
        dependency,
        tool: context.tool,
      },
      context,
      recoverable: true,
      suggestedActions: [
        `Ensure ${dependency} is properly configured`,
        'Check prerequisite setup steps',
        'Initialize required components first',
      ],
    });
  }

  private static sleep(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  /**
   * Get detailed information about a specific error
   */
  async getErrorDetails(errorId: string): Promise<any> {
    // This would normally fetch from a database
    return {
      errorId,
      type: 'validation',
      severity: 'medium',
      message: 'Sample error for development',
      timestamp: new Date().toISOString(),
      tool: 'unknown',
      context: {},
      stackTrace: null,
      rootCause: null,
      suggestions: [],
      relatedErrors: []
    };
  }

  /**
   * Analyze patterns in errors
   */
  async analyzeErrorPatterns(options: {
    timeRange?: string;
    errorTypes?: string[];
    minOccurrences?: number;
    groupBy?: string;
  }): Promise<any> {
    return {
      totalErrors: 0,
      patternsFound: [],
      recommendations: [],
      insights: []
    };
  }

  /**
   * Get timeline of errors
   */
  async getErrorTimeline(options: {
    timeRange?: string;
    toolName?: string;
    severity?: string;
    includeContext?: boolean;
  }): Promise<any[]> {
    return [];
  }

  /**
   * Generate error report
   */
  async generateErrorReport(options: {
    reportType?: string;
    timeRange?: string;
    includeRecommendations?: boolean;
    outputFormat?: string;
    saveToFile?: boolean;
  }): Promise<any> {
    const content = options.outputFormat === 'markdown' 
      ? '# Error Report\n\nNo errors to report.'
      : { errors: [], summary: 'No errors' };
    
    return {
      content,
      summary: { totalIssues: 0, critical: 0, high: 0, medium: 0, low: 0 },
      filePath: options.saveToFile ? '.atlas/reports/error-report.md' : undefined
    };
  }

  /**
   * Track error resolution
   */
  async resolveErrorSuggestion(options: {
    errorId: string;
    suggestionId: string;
    implementation?: string;
    effectiveness?: number;
    notes?: string;
  }): Promise<any> {
    return {
      errorId: options.errorId,
      suggestionId: options.suggestionId,
      implementation: options.implementation,
      effectiveness: options.effectiveness,
      notes: options.notes,
      outcome: { success: true, message: 'Tracked' }
    };
  }

  /**
   * Simulate error recovery
   */
  async simulateErrorRecovery(options: {
    errorType: string;
    severity?: string;
    context?: any;
    dryRun?: boolean;
  }): Promise<any> {
    return {
      errorType: options.errorType,
      severity: options.severity || 'medium',
      steps: [],
      results: { success: true, recoverable: true },
      recoveryTime: '0ms',
      effectiveness: '100%'
    };
  }
}

/**
 * Decorator for automatic error handling in tool functions
 */
export function withMCPErrorHandling(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = async function (...args: any[]) {
    try {
      return await originalMethod.apply(this, args);
    } catch (error) {
      const context = {
        tool: propertyKey,
        module: target.constructor.name,
        params: args[0],
      };
      throw ErrorHandler.transformError(error as Error, context);
    }
  };

  return descriptor;
}