/**
 * Authentication Error Handler Provides categorized error handling for OAuth and authentication
 * flows
 */

/** Error types for authentication failures */
const AUTH_ERROR_TYPES = {
  NETWORK_ERROR: 'NETWORK_ERROR',
  TIMEOUT: 'TIMEOUT',
  API_ERROR: 'API_ERROR',
  VALIDATION_ERROR: 'VALIDATION_ERROR',
  DATABASE_ERROR: 'DATABASE_ERROR',
  PROVIDER_ERROR: 'PROVIDER_ERROR',
  UNKNOWN: 'UNKNOWN',
};

/**
 * Determines the error type based on the error object
 *
 * @param {Error} error The error object to categorize
 * @returns {string} The error type
 */
function categorizeError(error) {
  if (!error) return AUTH_ERROR_TYPES.UNKNOWN;

  // Network and timeout errors
  if (error.code === 'ETIMEDOUT' || error.code === 'ESOCKETTIMEDOUT') {
    return AUTH_ERROR_TYPES.TIMEOUT;
  }

  if (
    error.code === 'ECONNREFUSED' ||
    error.code === 'ENOTFOUND' ||
    error.code === 'ECONNRESET' ||
    error.message?.includes('network') ||
    error.message?.includes('ECONNREFUSED')
  ) {
    return AUTH_ERROR_TYPES.NETWORK_ERROR;
  }

  // Axios/HTTP errors
  if (error.response) {
    if (error.response.status >= 500) {
      return AUTH_ERROR_TYPES.API_ERROR;
    }
    if (error.response.status >= 400 && error.response.status < 500) {
      return AUTH_ERROR_TYPES.PROVIDER_ERROR;
    }
  }

  // Database errors (Sequelize)
  if (error.name === 'SequelizeError' || error.name?.startsWith('Sequelize')) {
    return AUTH_ERROR_TYPES.DATABASE_ERROR;
  }

  // Validation errors
  if (error.name === 'ValidationError' || error.message?.includes('validation')) {
    return AUTH_ERROR_TYPES.VALIDATION_ERROR;
  }

  return AUTH_ERROR_TYPES.UNKNOWN;
}

/**
 * Gets a user-friendly error message based on error type
 *
 * @param {string} errorType The categorized error type
 * @param {string} provider The auth provider (google, facebook, etc.)
 * @returns {string} User-friendly error message
 */
function getUserFriendlyMessage(errorType, provider = 'authentication') {
  const messages = {
    [AUTH_ERROR_TYPES.NETWORK_ERROR]: `Unable to connect to ${provider}. Please check your internet connection and try again.`,
    [AUTH_ERROR_TYPES.TIMEOUT]: `Connection to ${provider} timed out. Please check your internet connection and try again.`,
    [AUTH_ERROR_TYPES.API_ERROR]: `${provider} service is temporarily unavailable. Please try again in a few moments.`,
    [AUTH_ERROR_TYPES.PROVIDER_ERROR]: `Authentication with ${provider} failed. Please try again or use a different sign-in method.`,
    [AUTH_ERROR_TYPES.DATABASE_ERROR]:
      'Unable to complete authentication due to a server error. Please try again.',
    [AUTH_ERROR_TYPES.VALIDATION_ERROR]: 'Invalid authentication data. Please try again.',
    [AUTH_ERROR_TYPES.UNKNOWN]: 'An unexpected error occurred. Please try again.',
  };

  return messages[errorType] || messages[AUTH_ERROR_TYPES.UNKNOWN];
}

/**
 * Determines if an error should trigger a retry
 *
 * @param {string} errorType The categorized error type
 * @returns {boolean} Whether the operation should be retried
 */
function shouldRetry(errorType) {
  const retryableErrors = [
    AUTH_ERROR_TYPES.NETWORK_ERROR,
    AUTH_ERROR_TYPES.TIMEOUT,
    AUTH_ERROR_TYPES.API_ERROR,
  ];

  return retryableErrors.includes(errorType);
}

/**
 * Creates a standardized error response for authentication failures
 *
 * @param {Error} error The original error
 * @param {string} provider The auth provider name
 * @returns {object} Standardized error response object
 */
function createAuthErrorResponse(error, provider = 'authentication') {
  const errorType = categorizeError(error);
  const message = getUserFriendlyMessage(errorType, provider);
  const canRetry = shouldRetry(errorType);

  return {
    success: false,
    error: {
      message,
      type: errorType,
      canRetry,
      provider,
    },
  };
}

/**
 * Wraps an async OAuth callback with error handling
 *
 * @param {(...args: unknown[]) => Promise<void>} callback The async callback function
 * @param {string} provider The provider name (google, facebook, etc.)
 * @returns {(...args: unknown[]) => Promise<void>} Wrapped callback with error handling
 */
function wrapOAuthCallback(callback, provider) {
  return async (...args) => {
    const done = args[args.length - 1];

    try {
      return await callback(...args);
    } catch (error) {
      const errorType = categorizeError(error);
      const message = getUserFriendlyMessage(errorType, provider);

      // Log error for debugging
      console.error(`[Auth Error] ${provider} OAuth failed:`, {
        type: errorType,
        message: error.message,
        stack: process.env.NODE_ENV === 'development' ? error.stack : undefined,
      });

      // Create error object with metadata
      const authError = new Error(message);
      authError.type = errorType;
      authError.canRetry = shouldRetry(errorType);
      authError.provider = provider;
      authError.originalError = error;

      return done(authError, null);
    }
  };
}

module.exports = {
  AUTH_ERROR_TYPES,
  categorizeError,
  createAuthErrorResponse,
  getUserFriendlyMessage,
  shouldRetry,
  wrapOAuthCallback,
};
