import { AxiosError } from 'axios';
import { isTimeoutError, TIMEOUT_MESSAGES } from './timeoutHandler';
import type { ApiError } from '@/types/api';

/**
 * Helper de gestion d'erreurs API
 * Transforme les erreurs Axios brutes en objets ApiError standardisés
 * selon le format défini dans FRONTEND_INTEGRATION.md
 */

/**
 * Parse une erreur Axios en ApiError standardisé
 * @param error - Erreur Axios ou Error
 * @returns ApiError formaté selon le contrat backend
 */
export function parseApiError(error: unknown): ApiError {
  // Si c'est déjà une ApiError, la retourner telle quelle
  if (isApiError(error)) {
    return error;
  }

  // Si c'est une erreur Axios
  if (isAxiosError(error)) {
    const axiosError = error as AxiosError;

    // Si le backend retourne le format standardisé { success: false, error: {...} }
    if (
      axiosError.response?.data &&
      typeof axiosError.response.data === 'object' &&
      'success' in axiosError.response.data &&
      axiosError.response.data.success === false &&
      'error' in axiosError.response.data
    ) {
      const backendError = (axiosError.response.data as { error: any }).error;
      return normalizeApiError(backendError);
    }

    // Si le backend retourne le format { error: {...} } sans success property (Middleware Gin actuel)
    if (
      axiosError.response?.data &&
      typeof axiosError.response.data === 'object' &&
      'error' in axiosError.response.data &&
      typeof (axiosError.response.data as any).error === 'object'
    ) {
      const backendError = (axiosError.response.data as { error: any }).error;
      // Vérifier si l'objet error contient au moins code ou message pour éviter les faux positifs
      if (backendError && ('code' in backendError || 'message' in backendError)) {
        return normalizeApiError(backendError);
      }
    }

    // Si le backend retourne directement un objet error (Legacy ou autre middleware)
    if (
      axiosError.response?.data &&
      typeof axiosError.response.data === 'object' &&
      'code' in axiosError.response.data &&
      'message' in axiosError.response.data
    ) {
      return normalizeApiError(axiosError.response.data);
    }

    // Erreur réseau (pas de réponse)
    if (axiosError.request && !axiosError.response) {
      // Check if it's a timeout error
      if (isTimeoutError(axiosError)) {
        return {
          code: 0,
          message: TIMEOUT_MESSAGES.timeout,
          timestamp: new Date().toISOString(),
        };
      }
      return {
        code: 0,
        message: 'Network error: Unable to connect to server',
        timestamp: new Date().toISOString(),
      };
    }

    // Gestion spécifique des codes HTTP d'erreur
    const status = axiosError.response?.status;

    if (status === 429) {
      // Too Many Requests - Rate limiting
      // INT-013: Enhanced rate limit error handling with headers
      const headers = axiosError.response?.headers || {};
      const data = axiosError.response?.data as any;
      
      // Extract rate limit information from headers
      const rateLimitLimit = headers['x-ratelimit-limit'] 
        ? parseInt(String(headers['x-ratelimit-limit']), 10) 
        : undefined;
      const rateLimitRemaining = headers['x-ratelimit-remaining'] 
        ? parseInt(String(headers['x-ratelimit-remaining']), 10) 
        : undefined;
      const rateLimitReset = headers['x-ratelimit-reset'] 
        ? parseInt(String(headers['x-ratelimit-reset']), 10) 
        : undefined;
      const retryAfter = headers['retry-after'] 
        ? parseInt(String(headers['retry-after']), 10) 
        : (data?.error?.retry_after || 60);
      
      // Calculate time until reset
      const resetTime = rateLimitReset ? new Date(rateLimitReset * 1000) : undefined;
      const secondsUntilReset = resetTime 
        ? Math.max(0, Math.ceil((resetTime.getTime() - Date.now()) / 1000))
        : retryAfter;
      
      return {
        code: 429,
        message:
          data?.error?.message ||
          'Trop de requêtes. Veuillez patienter avant de réessayer.',
        timestamp: new Date().toISOString(),
        details: [
          {
            field: 'rate_limit',
            message: `Limite de ${rateLimitLimit || 'N/A'} requêtes atteinte. Réessayez dans ${secondsUntilReset} seconde${secondsUntilReset > 1 ? 's' : ''}.`,
          },
          ...(rateLimitRemaining !== undefined
            ? [
                {
                  field: 'remaining',
                  message: `${rateLimitRemaining} requête${rateLimitRemaining > 1 ? 's' : ''} restante${rateLimitRemaining > 1 ? 's' : ''}`,
                },
              ]
            : []),
        ],
        retry_after: secondsUntilReset,
        rate_limit: {
          limit: rateLimitLimit,
          remaining: rateLimitRemaining,
          reset: resetTime?.toISOString(),
        },
      };
    }

    if (status === 503) {
      // Service Unavailable - ClamAV ou autre service externe
      return {
        code: 503,
        message:
          (axiosError.response?.data as any)?.message ||
          'Service temporairement indisponible. Veuillez réessayer dans quelques instants.',
        timestamp: new Date().toISOString(),
        details: (axiosError.response?.data as any)?.details,
      };
    }

    if (status === 502) {
      // Bad Gateway - Problème de communication avec un service externe
      return {
        code: 502,
        message:
          (axiosError.response?.data as any)?.message ||
          'Erreur de communication avec le serveur. Veuillez réessayer plus tard.',
        timestamp: new Date().toISOString(),
        details: (axiosError.response?.data as any)?.details,
      };
    }

    // Erreur HTTP sans format standardisé
    return {
      code: status || 0,
      message:
        (axiosError.response?.data as any)?.message ||
        axiosError.message ||
        'An unexpected error occurred',
      timestamp: new Date().toISOString(),
    };
  }

  // Erreur JavaScript standard
  if (error instanceof Error) {
    return {
      code: 0,
      message: error.message || 'An unexpected error occurred',
      timestamp: new Date().toISOString(),
    };
  }

  // Erreur inconnue
  return {
    code: 0,
    message: 'An unexpected error occurred',
    timestamp: new Date().toISOString(),
  };
}

/**
 * Normalise un objet d'erreur backend en ApiError standardisé
 */
function normalizeApiError(error: any): ApiError {
  return {
    code: typeof error.code === 'number' ? error.code : parseInt(String(error.code || 0), 10),
    message: error.message || 'An error occurred',
    details: error.details || (Array.isArray(error.details) ? error.details : undefined),
    request_id: error.request_id,
    timestamp: error.timestamp || new Date().toISOString(),
    context: error.context,
  };
}

/**
 * Formate un message d'erreur pour l'affichage dans l'UI
 * @param error - ApiError
 * @param includeRequestId - Si true, inclut le request_id dans le message (pour debugging)
 * @returns Message formaté pour l'utilisateur
 */
export function formatErrorMessage(
  error: ApiError,
  includeRequestId: boolean = false,
): string {
  let message = error.message;

  // Si l'erreur a des détails de validation, les inclure
  if (error.details && Array.isArray(error.details) && error.details.length > 0) {
    const detailsMessages = error.details
      .map((detail) => `${detail.field}: ${detail.message}`)
      .join(', ');
    message = `${error.message} (${detailsMessages})`;
  }

  // Optionnellement inclure le request_id pour le debugging (en mode développement)
  if (includeRequestId && error.request_id) {
    const isDev = import.meta.env.DEV;
    if (isDev) {
      message = `${message} [Request ID: ${error.request_id}]`;
    }
  }

  return message;
}

/**
 * Extrait les erreurs de validation par champ
 * @param error - ApiError
 * @returns Record avec les erreurs par champ (field -> message)
 */
export function getValidationErrors(
  error: ApiError,
): Record<string, string> {
  if (!error.details || !Array.isArray(error.details)) {
    return {};
  }

  const errors: Record<string, string> = {};
  for (const detail of error.details) {
    if (detail.field && detail.message) {
      errors[detail.field] = detail.message;
    }
  }

  return errors;
}

/**
 * Vérifie si une erreur est une ApiError
 */
function isApiError(error: unknown): error is ApiError {
  return (
    typeof error === 'object' &&
    error !== null &&
    'code' in error &&
    'message' in error &&
    typeof (error as any).code === 'number' &&
    typeof (error as any).message === 'string'
  );
}

/**
 * Vérifie si une erreur est une AxiosError
 */
function isAxiosError(error: unknown): error is AxiosError {
  return (
    typeof error === 'object' &&
    error !== null &&
    'isAxiosError' in error &&
    (error as any).isAxiosError === true
  );
}

