/**
 * Secure Error Handler Utility
 * Prevents CWE-209: Information Exposure Through Error Messages
 * 
 * This utility sanitizes error messages before sending them to clients,
 * ensuring that internal system details, file paths, stack traces, and
 * other sensitive information are never exposed to end users.
 */

const logger = require('./logger');

/**
 * Error types with user-friendly messages
 */
const ERROR_TYPES = {
  // Authentication & Authorization
  AUTH_FAILED: 'Authentication failed',
  AUTH_REQUIRED: 'Authentication required',
  AUTH_INVALID_TOKEN: 'Invalid or expired authentication token',
  AUTH_INSUFFICIENT_PERMISSIONS: 'Insufficient permissions',
  
  // User Management
  USER_NOT_FOUND: 'User not found',
  USER_ALREADY_EXISTS: 'User already exists',
  USER_CREATION_FAILED: 'Failed to create user',
  USER_UPDATE_FAILED: 'Failed to update user',
  USER_DELETE_FAILED: 'Failed to delete user',
  
  // Data Validation
  VALIDATION_FAILED: 'Validation failed',
  INVALID_INPUT: 'Invalid input provided',
  INVALID_FILE_TYPE: 'Invalid file type',
  FILE_TOO_LARGE: 'File size exceeds limit',
  MISSING_REQUIRED_FIELD: 'Required field is missing',
  
  // Database Operations
  DATABASE_ERROR: 'Database operation failed',
  RECORD_NOT_FOUND: 'Record not found',
  DUPLICATE_ENTRY: 'Duplicate entry exists',
  
  // File Operations
  FILE_NOT_FOUND: 'File not found',
  FILE_UPLOAD_FAILED: 'File upload failed',
  FILE_DELETE_FAILED: 'Failed to delete file',
  FILE_READ_FAILED: 'Failed to read file',
  FILE_WRITE_FAILED: 'Failed to write file',
  
  // Network & External Services
  NETWORK_ERROR: 'Network request failed',
  EXTERNAL_SERVICE_ERROR: 'External service unavailable',
  TIMEOUT_ERROR: 'Request timeout',
  
  // Rate Limiting
  RATE_LIMIT_EXCEEDED: 'Too many requests. Please try again later',
  
  // Generic
  INTERNAL_ERROR: 'An internal error occurred',
  NOT_FOUND: 'Resource not found',
  FORBIDDEN: 'Access forbidden',
  BAD_REQUEST: 'Bad request',
  CONFLICT: 'Resource conflict',
  UNPROCESSABLE_ENTITY: 'Unable to process request',
  SERVICE_UNAVAILABLE: 'Service temporarily unavailable'
};

/**
 * Sanitize error for client response
 * Removes sensitive information like stack traces, file paths, and internal details
 * 
 * @param {Error|string} error - The error to sanitize
 * @param {string} defaultMessage - Default message if error cannot be parsed
 * @returns {Object} Sanitized error object with safe message
 */
function sanitizeError(error, defaultMessage = ERROR_TYPES.INTERNAL_ERROR) {
  // If error is a string, return it as is (assuming it's already safe)
  if (typeof error === 'string') {
    return {
      message: error,
      code: 'CUSTOM_ERROR'
    };
  }

  // Extract error message
  const errorMessage = error?.message || defaultMessage;
  
  // Check for known error patterns and map to safe messages
  
  // Database errors
  if (errorMessage.includes('UNIQUE constraint') || errorMessage.includes('UNIQUE')) {
    return {
      message: ERROR_TYPES.DUPLICATE_ENTRY,
      code: 'DUPLICATE_ENTRY'
    };
  }
  
  if (errorMessage.includes('FOREIGN KEY constraint')) {
    return {
      message: ERROR_TYPES.CONFLICT,
      code: 'FOREIGN_KEY_CONSTRAINT'
    };
  }
  
  if (errorMessage.includes('NOT NULL constraint')) {
    return {
      message: ERROR_TYPES.MISSING_REQUIRED_FIELD,
      code: 'MISSING_FIELD'
    };
  }
  
  // File system errors
  if (errorMessage.includes('ENOENT') || errorMessage.includes('no such file')) {
    return {
      message: ERROR_TYPES.FILE_NOT_FOUND,
      code: 'FILE_NOT_FOUND'
    };
  }
  
  if (errorMessage.includes('EACCES') || errorMessage.includes('permission denied')) {
    return {
      message: ERROR_TYPES.FORBIDDEN,
      code: 'PERMISSION_DENIED'
    };
  }
  
  if (errorMessage.includes('ENOSPC') || errorMessage.includes('no space')) {
    return {
      message: ERROR_TYPES.SERVICE_UNAVAILABLE,
      code: 'DISK_FULL'
    };
  }
  
  // Network errors
  if (errorMessage.includes('ECONNREFUSED') || errorMessage.includes('connection refused')) {
    return {
      message: ERROR_TYPES.EXTERNAL_SERVICE_ERROR,
      code: 'CONNECTION_REFUSED'
    };
  }
  
  if (errorMessage.includes('ETIMEDOUT') || errorMessage.includes('timeout')) {
    return {
      message: ERROR_TYPES.TIMEOUT_ERROR,
      code: 'TIMEOUT'
    };
  }
  
  if (errorMessage.includes('ENOTFOUND') || errorMessage.includes('getaddrinfo')) {
    return {
      message: ERROR_TYPES.NETWORK_ERROR,
      code: 'DNS_ERROR'
    };
  }
  
  // Authentication errors
  if (errorMessage.toLowerCase().includes('unauthorized') || 
      errorMessage.toLowerCase().includes('authentication')) {
    return {
      message: ERROR_TYPES.AUTH_FAILED,
      code: 'AUTH_ERROR'
    };
  }
  
  if (errorMessage.toLowerCase().includes('forbidden') || 
      errorMessage.toLowerCase().includes('permission')) {
    return {
      message: ERROR_TYPES.AUTH_INSUFFICIENT_PERMISSIONS,
      code: 'PERMISSION_ERROR'
    };
  }
  
  // Validation errors (pass through if they seem safe)
  if (errorMessage.toLowerCase().includes('validation') ||
      errorMessage.toLowerCase().includes('invalid')) {
    // Check if message is reasonably safe (no paths, no system info)
    if (!containsSensitiveInfo(errorMessage)) {
      return {
        message: errorMessage,
        code: 'VALIDATION_ERROR'
      };
    }
    return {
      message: ERROR_TYPES.VALIDATION_FAILED,
      code: 'VALIDATION_ERROR'
    };
  }
  
  // Default to generic error message
  return {
    message: defaultMessage,
    code: 'INTERNAL_ERROR'
  };
}

/**
 * Check if error message contains sensitive information
 * 
 * @param {string} message - Error message to check
 * @returns {boolean} True if message contains sensitive info
 */
function containsSensitiveInfo(message) {
  const sensitivePatterns = [
    /\/[a-z0-9_\-\/]+\.(js|json|db|log|conf|env)/i, // File paths
    /at\s+[a-zA-Z0-9_]+\s+\(/i, // Stack trace patterns
    /line\s+\d+/i, // Line numbers
    /column\s+\d+/i, // Column numbers
    /Error:\s+SQLITE_/i, // SQLite internal errors
    /node_modules/i, // Node modules paths
    /\/home\//i, // Unix home directory
    /\/usr\//i, // Unix system paths
    /\/var\//i, // Unix var paths
    /\/tmp\//i, // Temp directory
    /C:\\/i, // Windows paths
    /\\Users\\/i, // Windows user paths
    /password/i, // Password references
    /secret/i, // Secret references
    /token/i, // Token references
    /key/i // Key references (be careful with "keyboard" etc.)
  ];
  
  return sensitivePatterns.some(pattern => pattern.test(message));
}

/**
 * Log error securely (internal logs can contain full details)
 * 
 * @param {Error|string} error - Error to log
 * @param {Object} context - Additional context
 */
function logError(error, context = {}) {
  const errorInfo = {
    message: error?.message || error,
    stack: error?.stack,
    code: error?.code,
    ...context
  };
  
  logger.error('Application error:', errorInfo);
}

/**
 * Express error handler middleware
 * Catches all errors and returns sanitized responses
 * 
 * @param {Error} err - Error object
 * @param {Object} req - Express request
 * @param {Object} res - Express response
 * @param {Function} next - Express next function
 */
function errorMiddleware(err, req, res, next) {
  // Log full error details internally
  logError(err, {
    method: req.method,
    path: req.path,
    userId: req.user?.id,
    ip: req.ip
  });
  
  // Determine status code
  const statusCode = err.statusCode || err.status || 500;
  
  // Sanitize error for client
  const sanitized = sanitizeError(err, ERROR_TYPES.INTERNAL_ERROR);
  
  // Send sanitized response
  res.status(statusCode).json({
    error: sanitized.message,
    code: sanitized.code,
    timestamp: new Date().toISOString()
  });
}

/**
 * Async handler wrapper to catch async errors
 * 
 * @param {Function} fn - Async route handler
 * @returns {Function} Wrapped function
 */
function asyncHandler(fn) {
  return (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch(next);
  };
}

/**
 * Create a safe error response object
 * 
 * @param {string} message - Error message (should be user-safe)
 * @param {number} statusCode - HTTP status code
 * @param {string} code - Error code
 * @returns {Object} Error response object
 */
function createError(message, statusCode = 500, code = 'ERROR') {
  const error = new Error(message);
  error.statusCode = statusCode;
  error.code = code;
  return error;
}

/**
 * Standard error responses
 */
const ErrorResponses = {
  badRequest: (message = ERROR_TYPES.BAD_REQUEST) => 
    createError(message, 400, 'BAD_REQUEST'),
  
  unauthorized: (message = ERROR_TYPES.AUTH_REQUIRED) => 
    createError(message, 401, 'UNAUTHORIZED'),
  
  forbidden: (message = ERROR_TYPES.FORBIDDEN) => 
    createError(message, 403, 'FORBIDDEN'),
  
  notFound: (message = ERROR_TYPES.NOT_FOUND) => 
    createError(message, 404, 'NOT_FOUND'),
  
  conflict: (message = ERROR_TYPES.CONFLICT) => 
    createError(message, 409, 'CONFLICT'),
  
  unprocessable: (message = ERROR_TYPES.UNPROCESSABLE_ENTITY) => 
    createError(message, 422, 'UNPROCESSABLE_ENTITY'),
  
  tooManyRequests: (message = ERROR_TYPES.RATE_LIMIT_EXCEEDED) => 
    createError(message, 429, 'TOO_MANY_REQUESTS'),
  
  internal: (message = ERROR_TYPES.INTERNAL_ERROR) => 
    createError(message, 500, 'INTERNAL_ERROR'),
  
  serviceUnavailable: (message = ERROR_TYPES.SERVICE_UNAVAILABLE) => 
    createError(message, 503, 'SERVICE_UNAVAILABLE')
};

module.exports = {
  ERROR_TYPES,
  sanitizeError,
  containsSensitiveInfo,
  logError,
  errorMiddleware,
  asyncHandler,
  createError,
  ErrorResponses
};
