/**
 * Error Messages Utility
 * FE-API-013: Centralized user-friendly error messages
 *
 * Provides consistent, user-friendly error messages across the application
 * with support for internationalization and context-aware messages.
 */

import type { ApiError } from '@/schemas/apiSchemas';

/**
 * User-friendly error messages mapped by HTTP status codes
 */
export const ERROR_MESSAGES = {
  // Client errors (4xx)
  400: 'La requête est invalide. Veuillez vérifier les informations fournies.',
  401: 'Vous devez être connecté pour effectuer cette action.',
  403: "Vous n'avez pas les permissions nécessaires pour effectuer cette action.",
  404: 'La ressource demandée est introuvable.',
  409: 'Un conflit est survenu. Cette ressource existe déjà ou a été modifiée.',
  422: 'Les données fournies ne sont pas valides.',
  429: 'Trop de requêtes. Veuillez patienter quelques instants avant de réessayer.',

  // Server errors (5xx)
  500: "Une erreur serveur s'est produite. Veuillez réessayer plus tard.",
  502: 'Erreur de communication avec le serveur. Veuillez réessayer plus tard.',
  503: 'Service temporairement indisponible. Veuillez réessayer dans quelques instants.',
  504: 'Le serveur met trop de temps à répondre. Veuillez réessayer plus tard.',

  // Network errors
  NETWORK:
    'Erreur de connexion. Vérifiez votre connexion internet et réessayez. Si le problème persiste, le serveur pourrait être temporairement indisponible.',
  TIMEOUT:
    'La requête a expiré. Vérifiez votre connexion internet et réessayez.',
  UNKNOWN: "Une erreur inattendue s'est produite. Veuillez réessayer.",
} as const;

/**
 * Context-specific error messages
 */
export const CONTEXT_ERROR_MESSAGES = {
  auth: {
    login: 'Échec de la connexion. Vérifiez vos identifiants.',
    logout: 'Erreur lors de la déconnexion.',
    register: "Erreur lors de l'inscription. Veuillez réessayer.",
    tokenExpired: 'Votre session a expiré. Veuillez vous reconnecter.',
  },
  upload: {
    fileTooLarge: 'Le fichier est trop volumineux.',
    invalidFormat: "Le format de fichier n'est pas supporté.",
    uploadFailed: "L'upload a échoué. Veuillez réessayer.",
    networkError: "Erreur réseau lors de l'upload. Vérifiez votre connexion.",
  },
  playlist: {
    notFound: 'La playlist est introuvable.',
    accessDenied: "Vous n'avez pas accès à cette playlist.",
    createFailed: 'Erreur lors de la création de la playlist.',
    updateFailed: 'Erreur lors de la mise à jour de la playlist.',
    deleteFailed: 'Erreur lors de la suppression de la playlist.',
  },
  track: {
    notFound: 'Le morceau est introuvable.',
    playFailed: 'Impossible de lire le morceau. Vérifiez votre connexion.',
    uploadFailed: "Erreur lors de l'upload du morceau.",
    deleteFailed: 'Erreur lors de la suppression du morceau.',
  },
  conversation: {
    notFound: 'La conversation est introuvable.',
    accessDenied: "Vous n'avez pas accès à cette conversation.",
    createFailed: 'Erreur lors de la création de la conversation.',
    sendMessageFailed: "Erreur lors de l'envoi du message.",
  },
  search: {
    failed: 'La recherche a échoué. Veuillez réessayer.',
    timeout: 'La recherche a pris trop de temps. Veuillez réessayer.',
    invalidQuery: 'La requête de recherche est invalide.',
  },
} as const;

/**
 * Gets a user-friendly error message based on status code
 * @param status HTTP status code
 * @param defaultMessage Optional default message if status not found
 * @returns User-friendly error message
 */
export function getErrorMessageByStatus(
  status: number,
  defaultMessage?: string,
): string {
  if (status in ERROR_MESSAGES) {
    return ERROR_MESSAGES[status as keyof typeof ERROR_MESSAGES];
  }
  return defaultMessage || ERROR_MESSAGES.UNKNOWN;
}

/**
 * Gets a context-specific error message
 * @param context Context key (e.g., 'auth', 'upload')
 * @param action Action key (e.g., 'login', 'uploadFailed')
 * @param defaultMessage Optional default message if context/action not found
 * @returns Context-specific error message
 */
export function getContextErrorMessage(
  context: keyof typeof CONTEXT_ERROR_MESSAGES,
  action: string,
  defaultMessage?: string,
): string {
  const contextMessages = CONTEXT_ERROR_MESSAGES[context];
  if (contextMessages && action in contextMessages) {
    return (contextMessages as any)[action];
  }
  return defaultMessage || ERROR_MESSAGES.UNKNOWN;
}

/**
 * Formats an ApiError into a user-friendly message
 * @param error ApiError object
 * @param context Optional context for context-specific messages
 * @param includeDetails Whether to include validation details in the message
 * @returns Formatted user-friendly error message
 */
export function formatUserFriendlyError(
  error: ApiError | Error | unknown,
  context?: keyof typeof CONTEXT_ERROR_MESSAGES,
  includeDetails: boolean = false,
): string {
  // Handle ApiError
  if (
    error &&
    typeof error === 'object' &&
    'code' in error &&
    'message' in error
  ) {
    const apiError = error as ApiError;
    const status = typeof apiError.code === 'number' ? apiError.code : 0;

    // Try context-specific message first
    if (context && status >= 400 && status < 500) {
      // Try to extract action from error message or use status
      const action = extractActionFromMessage(apiError.message);
      const contextMessage = getContextErrorMessage(context, action, undefined);
      if (contextMessage !== ERROR_MESSAGES.UNKNOWN) {
        return contextMessage;
      }
    }

    // Use status-based message
    if (status > 0) {
      const statusMessage = getErrorMessageByStatus(status, apiError.message);

      // Add validation details if requested
      if (
        includeDetails &&
        apiError.details &&
        Array.isArray(apiError.details)
      ) {
        const details = apiError.details
          .map((d: any) => d.message || d.field)
          .filter(Boolean)
          .join(', ');
        if (details) {
          return `${statusMessage} (${details})`;
        }
      }

      return statusMessage;
    }

    // Fallback to error message
    return apiError.message || ERROR_MESSAGES.UNKNOWN;
  }

  // Handle standard Error
  if (error instanceof Error) {
    return error.message || ERROR_MESSAGES.UNKNOWN;
  }

  // Handle network errors
  if (error && typeof error === 'object' && 'code' in error) {
    const code = (error as any).code;
    if (code === 'ECONNABORTED' || code === 'ETIMEDOUT') {
      return ERROR_MESSAGES.TIMEOUT;
    }
    if (code === 'ERR_NETWORK' || !(error as any).response) {
      return ERROR_MESSAGES.NETWORK;
    }
  }

  return ERROR_MESSAGES.UNKNOWN;
}

/**
 * Extracts action from error message for context matching
 * @param message Error message
 * @returns Extracted action key
 */
function extractActionFromMessage(message: string): string {
  const lowerMessage = message.toLowerCase();

  if (lowerMessage.includes('login') || lowerMessage.includes('connexion')) {
    return 'login';
  }
  if (lowerMessage.includes('logout') || lowerMessage.includes('déconnexion')) {
    return 'logout';
  }
  if (
    lowerMessage.includes('register') ||
    lowerMessage.includes('inscription')
  ) {
    return 'register';
  }
  if (
    lowerMessage.includes('upload') ||
    lowerMessage.includes('téléchargement')
  ) {
    if (lowerMessage.includes('large') || lowerMessage.includes('volumineux')) {
      return 'fileTooLarge';
    }
    if (lowerMessage.includes('format') || lowerMessage.includes('type')) {
      return 'invalidFormat';
    }
    return 'uploadFailed';
  }
  if (
    lowerMessage.includes('not found') ||
    lowerMessage.includes('introuvable')
  ) {
    return 'notFound';
  }
  if (
    lowerMessage.includes('access denied') ||
    lowerMessage.includes('permission')
  ) {
    return 'accessDenied';
  }
  if (lowerMessage.includes('create') || lowerMessage.includes('créer')) {
    return 'createFailed';
  }
  if (lowerMessage.includes('update') || lowerMessage.includes('mise à jour')) {
    return 'updateFailed';
  }
  if (lowerMessage.includes('delete') || lowerMessage.includes('suppression')) {
    return 'deleteFailed';
  }

  return '';
}

/**
 * Checks if an error is retryable
 * @param error Error object
 * @returns True if the error is retryable
 */
export function isRetryableError(error: unknown): boolean {
  if (error && typeof error === 'object' && 'code' in error) {
    const apiError = error as ApiError;
    const status = typeof apiError.code === 'number' ? apiError.code : 0;

    // Retryable status codes: 429, 500, 502, 503, 504
    if ([429, 500, 502, 503, 504].includes(status)) {
      return true;
    }

    // Network errors are retryable
    const code = (error as any).code;
    if (
      code === 'ECONNABORTED' ||
      code === 'ETIMEDOUT' ||
      code === 'ERR_NETWORK'
    ) {
      return true;
    }
  }

  return false;
}

/**
 * Gets retry delay in milliseconds based on error
 * @param error Error object
 * @param attempt Current retry attempt (0-indexed)
 * @returns Delay in milliseconds
 */
export function getRetryDelay(error: unknown, attempt: number = 0): number {
  if (error && typeof error === 'object' && 'code' in error) {
    const apiError = error as ApiError;
    const status = typeof apiError.code === 'number' ? apiError.code : 0;

    // Rate limit: use retry_after if available
    if (status === 429 && 'retry_after' in apiError) {
      const retryAfter = (apiError as any).retry_after;
      if (typeof retryAfter === 'number') {
        return retryAfter * 1000;
      }
    }
  }

  // Exponential backoff: 1s, 2s, 4s, 8s, max 30s
  return Math.min(1000 * Math.pow(2, attempt), 30000);
}
