import axios, { AxiosError, InternalAxiosRequestConfig, AxiosResponse } from 'axios';
import toast from 'react-hot-toast';
import { z } from 'zod';
import { TokenStorage } from '../tokenStorage';
import { refreshToken } from '../tokenRefresh';
import { env } from '@/config/env';
import { parseApiError } from '@/utils/apiErrorHandler';
import { csrfService } from '../csrf';
import { logger } from '@/utils/logger';
import { isTimeoutError, getTimeoutMessage } from '@/utils/timeoutHandler';
import { offlineQueue } from '../offlineQueue';
import { requestDeduplication } from '../requestDeduplication';
import { responseCache } from '../responseCache';
import { invalidateStateAfterMutation } from '@/utils/stateInvalidation';
import { safeValidateApiResponse } from '@/schemas/apiSchemas';
import { safeValidateApiRequest } from '@/schemas/apiRequestSchemas';
import type { ApiResponse } from '@/types/api';

/**
 * Client API avec interceptors pour refresh automatique des tokens,
 * unwrapping du format backend { success, data, error },
 * et retry automatique avec exponential backoff
 * Aligné avec FRONTEND_INTEGRATION.md
 */

// Client API réutilisable
export const apiClient = axios.create({
  baseURL: env.API_URL,
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
  },
});

// Flag pour éviter les refresh en boucle
let isRefreshing = false;
let failedQueue: Array<{
  resolve: (value?: any) => void;
  reject: (error?: any) => void;
}> = [];

/**
 * Sleep utility function
 */
const sleep = (ms: number): Promise<void> => {
  return new Promise((resolve) => setTimeout(resolve, ms));
};


/**
 * Retry configuration
 */
interface RetryConfig {
  maxRetries: number;
  baseDelay: number;
  maxDelay: number;
  retryableStatusCodes: number[];
  retryableNetworkErrors: string[];
}

const DEFAULT_RETRY_CONFIG: RetryConfig = {
  maxRetries: 3,
  baseDelay: 1000, // 1 second
  maxDelay: 10000, // 10 seconds
  retryableStatusCodes: [429, 500, 502, 503, 504], // Rate limit, server errors, gateway errors
  retryableNetworkErrors: [
    'ECONNABORTED', // Timeout
    'ETIMEDOUT', // Timeout
    'ENOTFOUND', // DNS error
    'ECONNREFUSED', // Connection refused
    'ECONNRESET', // Connection reset
    'EAI_AGAIN', // DNS lookup failed
    'Network Error', // Generic network error
  ],
};

/**
 * Check if a request method is idempotent (safe to retry)
 */
const isIdempotentMethod = (method?: string): boolean => {
  const idempotentMethods = ['GET', 'HEAD', 'OPTIONS'];
  return method ? idempotentMethods.includes(method.toUpperCase()) : false;
};

/**
 * Check if an error is retryable
 */
const isRetryableError = (error: AxiosError, config: RetryConfig = DEFAULT_RETRY_CONFIG): boolean => {
  // Don't retry if request was cancelled
  if (axios.isCancel(error)) {
    return false;
  }

  // Check if retry is disabled for this request
  if ((error.config as any)?._disableRetry) {
    return false;
  }

  // Check status code
  if (error.response?.status) {
    return config.retryableStatusCodes.includes(error.response.status);
  }

  // Check network errors
  if (error.code) {
    return config.retryableNetworkErrors.includes(error.code);
  }

  // Check error message for network-related errors
  if (error.message) {
    const message = error.message.toLowerCase();
    const networkErrorPatterns = ['network', 'timeout', 'connection', 'econn', 'etimedout', 'enotfound'];
    return networkErrorPatterns.some((pattern) => message.includes(pattern));
  }

  // For errors without response (network errors), retry if it's an idempotent method
  if (!error.response && error.request) {
    return isIdempotentMethod(error.config?.method);
  }

  return false;
};

/**
 * Get retry delay from Retry-After header or use exponential backoff with jitter
 */
const getRetryDelay = (
  error: AxiosError,
  attempt: number,
  baseDelay: number = DEFAULT_RETRY_CONFIG.baseDelay,
  maxDelay: number = DEFAULT_RETRY_CONFIG.maxDelay,
): number => {
  // Check for Retry-After header (case-insensitive)
  const retryAfterHeader =
    error.response?.headers['retry-after'] ||
    error.response?.headers['Retry-After'];
  if (retryAfterHeader) {
    const delay = parseInt(String(retryAfterHeader), 10);
    if (!isNaN(delay) && delay > 0) {
      return Math.min(delay * 1000, maxDelay); // Convert to milliseconds, cap at maxDelay
    }
  }

  // Exponential backoff with jitter: baseDelay * 2^attempt + random(0, baseDelay)
  const exponentialDelay = baseDelay * Math.pow(2, attempt);
  const jitter = Math.random() * baseDelay; // Add jitter to avoid thundering herd
  return Math.min(exponentialDelay + jitter, maxDelay);
};

/**
 * Sanitize sensitive data from request/response for logging
 */
const sanitizeForLogging = (data: any): any => {
  if (!data || typeof data !== 'object') {
    return data;
  }

  const sensitiveKeys = ['password', 'token', 'access_token', 'refresh_token', 'secret', 'authorization', 'x-csrf-token'];
  const sanitized = Array.isArray(data) ? [...data] : { ...data };

  for (const key in sanitized) {
    const lowerKey = key.toLowerCase();
    if (sensitiveKeys.some((sk) => lowerKey.includes(sk))) {
      sanitized[key] = '[REDACTED]';
    } else if (typeof sanitized[key] === 'object' && sanitized[key] !== null) {
      sanitized[key] = sanitizeForLogging(sanitized[key]);
    }
  }

  return sanitized;
};

/**
 * Get request ID from headers or generate one
 */
const getRequestId = (config: InternalAxiosRequestConfig): string => {
  // Try to get request_id from headers (if set by caller)
  const requestId = (config.headers as any)?.['X-Request-ID'] || 
                    (config.headers as any)?.['x-request-id'] ||
                    `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  
  // Store in config for later use
  (config as any)._requestId = requestId;
  
  return requestId;
};

// T0177: Fonction pour traiter la queue de requêtes en attente
const processQueue = (error: Error | null, token: string | null = null) => {
  failedQueue.forEach((prom) => {
    if (error) {
      prom.reject(error);
    } else {
      prom.resolve(token);
    }
  });

  failedQueue = [];
};

// T0177: Interceptor de requête pour ajouter le token d'accès
// CRITIQUE: Récupérer TOUJOURS le token frais depuis localStorage car Zustand peut ne pas être hydraté
apiClient.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    const token = TokenStorage.getAccessToken();

    if (token && config.headers) {
      config.headers.Authorization = `Bearer ${token} `;
    }

    // Pour FormData, laisser Axios gérer automatiquement le Content-Type avec boundary
    // Ne pas forcer application/json si c'est un FormData
    if (config.data instanceof FormData && config.headers) {
      // Supprimer Content-Type pour que Axios calcule automatiquement multipart/form-data avec boundary
      delete config.headers['Content-Type'];
    }

    // Ajouter le token CSRF pour les méthodes qui modifient l'état
    const method = config.method?.toUpperCase();
    const isStateChanging = ['POST', 'PUT', 'DELETE', 'PATCH'].includes(method || '');
    const isCSRFRoute = config.url?.includes('/csrf-token');
    
    if (isStateChanging && !isCSRFRoute && config.headers) {
      const csrfToken = csrfService.getToken();
      if (csrfToken) {
        config.headers['X-CSRF-Token'] = csrfToken;
      }
    }

    // Support AbortController: si un signal est fourni dans la config, l'utiliser
    // Sinon, créer un nouveau AbortController si nécessaire
    if (!config.signal && !config.cancelToken) {
      // Si aucune annulation n'est configurée, on peut créer un AbortController optionnel
      // Mais on ne le fait pas automatiquement pour éviter de créer des signaux inutiles
      // Les utilisateurs peuvent passer un signal via config.signal
    }

    // FE-TYPE-003: Validate request data if schema is provided
    const requestSchema = (config as any)?._requestSchema as z.ZodSchema | undefined;
    if (requestSchema && config.data !== undefined && config.data !== null) {
      // Skip validation for FormData (file uploads)
      if (!(config.data instanceof FormData)) {
        const validation = safeValidateApiRequest(requestSchema, config.data);
        if (!validation.success) {
          logger.warn('[API] Request validation failed:', {
            url: config.url,
            errors: validation.error?.errors,
          });
          // In development, log full error details
          if (import.meta.env.DEV) {
            console.warn('[API Request Validation Error]', validation.error);
          }
          // Throw error to prevent invalid request from being sent
          throw new Error(`Request validation failed: ${validation.error?.errors.map(e => e.message).join(', ')}`);
        }
        // Use validated data
        config.data = validation.data;
      }
    }

    // Store request start time for duration calculation
    (config as any)._requestStartTime = Date.now();

    // Log request (only in development or if explicitly enabled)
    if (import.meta.env.DEV || (config as any)?._enableLogging) {
      const requestId = getRequestId(config);
      const sanitizedHeaders = sanitizeForLogging({ ...config.headers });
      const sanitizedData = sanitizeForLogging(config.data);
      
      logger.debug(`[API Request] ${method || 'GET'} ${config.url}`, {
        request_id: requestId,
        method: method || 'GET',
        url: config.url,
        baseURL: config.baseURL,
        headers: sanitizedHeaders,
        params: config.params,
        data: sanitizedData,
        timeout: config.timeout,
        signal: config.signal ? 'AbortController' : undefined,
      });
    }

    return config;
  },
  (error) => {
    // Log request error
    if (import.meta.env.DEV) {
      logger.error('[API Request Error]', {
        error: error.message,
        config: error.config ? {
          url: error.config.url,
          method: error.config.method,
        } : undefined,
      });
    }
    return Promise.reject(error);
  },
);

// Interceptor de réponse pour unwrap le format backend et gérer les erreurs
apiClient.interceptors.response.use(
  (response: AxiosResponse<ApiResponse<any> | any>) => {
    // Log successful response (only in development or if explicitly enabled)
    const requestId = (response.config as any)?._requestId;
    const shouldLog = import.meta.env.DEV || (response.config as any)?._enableLogging;
    
    if (shouldLog) {
      const sanitizedData = sanitizeForLogging(response.data);
      const sanitizedHeaders = sanitizeForLogging(response.headers);
      
      logger.debug(`[API Response] ${response.config.method?.toUpperCase() || 'GET'} ${response.config.url} ${response.status}`, {
        request_id: requestId,
        status: response.status,
        statusText: response.statusText,
        headers: sanitizedHeaders,
        data: sanitizedData,
        duration: (response.config as any)?._requestStartTime 
          ? Date.now() - (response.config as any)._requestStartTime 
          : undefined,
      });
    }

    // Backend peut retourner plusieurs formats :
    // 1. Format standard avec wrapper: { success: true, data: {...} }
    // 2. Format direct JSON: { tracks: [...], pagination: {...} } (ex: SearchTracks, ListTracks)
    // 3. Format avec message: { success: true, data: {...}, message: "..." }
    
    if (!response.data || typeof response.data !== 'object') {
      // Si response.data n'est pas un objet, retourner tel quel
      return response;
    }

    // FE-COMP-005: Show success toast for mutation operations if enabled
    const method = response.config.method?.toUpperCase();
    const isMutation = ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method || '');
    const shouldShowSuccessToast = isMutation && 
                                    (response.config as any)?._showSuccessToast &&
                                    typeof window !== 'undefined';

    if (shouldShowSuccessToast) {
      const successMessage = (response.config as any)?._successMessage || 
                            (response.data as any)?.message ||
                            getDefaultSuccessMessage(method || '');
      
      if (successMessage) {
        toast.success(successMessage);
      }
    }

    // FE-API-017: Cache GET responses
    if (method === 'GET' && !(response.config as any)?._disableCache) {
      responseCache.set(response.config, response);
    }

    // FE-API-017: Invalidate cache on mutations
    // FE-STATE-004: Invalidate state after mutations
    if (isMutation) {
      const url = response.config.url || '';
      const method = response.config.method || 'POST';
      
      // Use centralized invalidation system
      invalidateStateAfterMutation(url, method);
    }

    // Vérifier si c'est le format wrapper avec success
    if ('success' in response.data) {
      if (response.data.success === true) {
        // Format wrapper standard: { success: true, data: {...} }
        // On unwrap pour retourner directement data
        // Si data est null/undefined, on retourne null au lieu de undefined
        const unwrappedData = response.data.data !== undefined ? response.data.data : null;
        
        // FE-TYPE-002: Validate response data if schema is provided
        const responseSchema = (response.config as any)?._responseSchema as z.ZodSchema | undefined;
        if (responseSchema && unwrappedData !== null) {
          const validation = safeValidateApiResponse(responseSchema, unwrappedData);
          if (!validation.success) {
            logger.warn('[API] Response validation failed:', {
              url: response.config.url,
              errors: validation.error?.errors,
            });
            // In development, log full error details
            if (import.meta.env.DEV) {
              console.warn('[API Validation Error]', validation.error);
            }
            // Continue with unvalidated data (don't break the app)
            // In production, you might want to throw or handle differently
          }
        }
        
        return {
          ...response,
          data: unwrappedData,
        } as AxiosResponse<any>;
      }
      // Si success === false, l'erreur sera gérée par le catch
      // Mais on devrait normalement ne jamais arriver ici car le backend
      // retourne un status HTTP d'erreur dans ce cas
    }
    
    // Si pas de format wrapper (format direct JSON), retourner la réponse telle quelle
    // Exemples: { tracks: [...], pagination: {...} }, { user: {...}, token: {...} }
    // FE-TYPE-002: Validate direct format responses if schema is provided
    const responseSchema = (response.config as any)?._responseSchema as z.ZodSchema | undefined;
    if (responseSchema && response.data) {
      const validation = safeValidateApiResponse(responseSchema, response.data);
      if (!validation.success) {
        logger.warn('[API] Response validation failed:', {
          url: response.config.url,
          errors: validation.error?.errors,
        });
        if (import.meta.env.DEV) {
          console.warn('[API Validation Error]', validation.error);
        }
      }
    }
    
    return response;
  },
  async (error: AxiosError<ApiResponse<any>>) => {
    // Don't retry or process cancelled requests
    if (axios.isCancel(error)) {
      const requestId = (error.config as any)?._requestId;
      if (import.meta.env.DEV || (error.config as any)?._enableLogging) {
        logger.debug(`[API Request Cancelled] ${error.config?.method?.toUpperCase() || 'GET'} ${error.config?.url}`, {
          request_id: requestId,
        });
      }
      return Promise.reject(error);
    }

    const originalRequest = error.config as InternalAxiosRequestConfig & {
      _retry?: boolean;
    };
    
    // Log error response (only in development or if explicitly enabled)
    const requestId = (originalRequest as any)?._requestId;
    const shouldLog = import.meta.env.DEV || (originalRequest as any)?._enableLogging;
    
    if (shouldLog && error.response) {
      const sanitizedErrorData = sanitizeForLogging(error.response.data);
      const sanitizedHeaders = sanitizeForLogging(error.response.headers);
      
      logger.error(`[API Error Response] ${originalRequest?.method?.toUpperCase() || 'GET'} ${originalRequest?.url} ${error.response.status}`, {
        request_id: requestId,
        status: error.response.status,
        statusText: error.response.statusText,
        headers: sanitizedHeaders,
        data: sanitizedErrorData,
        duration: (originalRequest as any)?._requestStartTime 
          ? Date.now() - (originalRequest as any)._requestStartTime 
          : undefined,
      });
    } else if (shouldLog && error.request && !error.response) {
      // Network error (no response received)
      logger.error(`[API Network Error] ${originalRequest?.method?.toUpperCase() || 'GET'} ${originalRequest?.url}`, {
        request_id: requestId,
        message: error.message,
        code: error.code,
        duration: (originalRequest as any)?._requestStartTime 
          ? Date.now() - (originalRequest as any)._requestStartTime 
          : undefined,
      });
    }

    // Détecter 401 et refresh automatiquement
    // EXCLURE l'endpoint /auth/refresh pour éviter les boucles infinies
    const isRefreshEndpoint = originalRequest?.url?.includes('/auth/refresh');

    if (
      error.response?.status === 401 &&
      originalRequest &&
      !originalRequest._retry &&
      !isRefreshEndpoint
    ) {
      // Éviter les refresh multiples simultanés
      if (isRefreshing) {
        // Si un refresh est en cours, mettre la requête en queue
        return new Promise((resolve, reject) => {
          failedQueue.push({ resolve, reject });
        })
          .then((token) => {
            if (originalRequest.headers && token) {
              originalRequest.headers.Authorization = `Bearer ${token} `;
            }
            return apiClient(originalRequest);
          })
          .catch((err) => {
            return Promise.reject(err);
          });
      }

      originalRequest._retry = true;
      isRefreshing = true;

      try {
        // Refresh automatique du token
        await refreshToken();
        const newToken = TokenStorage.getAccessToken();

        if (!newToken) {
          throw new Error('Failed to get new access token after refresh');
        }

        if (originalRequest.headers) {
          originalRequest.headers.Authorization = `Bearer ${newToken} `;
        }

        // Traiter la queue et retry la requête originale
        processQueue(null, newToken);
        return apiClient(originalRequest);
      } catch (refreshError) {
        // Gérer cas refresh échoué
        processQueue(refreshError as Error, null);

        // Nettoyer les tokens
        TokenStorage.clearTokens();

        // Stocker un message d'erreur pour l'afficher après redirection
        if (typeof window !== 'undefined') {
          sessionStorage.setItem(
            'auth_error',
            'Your session has expired. Please log in again.',
          );
          // Rediriger vers login si refresh échoue (seulement dans le navigateur)
          window.location.href = '/login';
        }

        return Promise.reject(refreshError);
      } finally {
        isRefreshing = false;
      }
    }

    // Unified retry logic for all retryable errors
    const status = error.response?.status;
    const retryCount = (originalRequest as any)?._retryCount || 0;
    const maxRetries = DEFAULT_RETRY_CONFIG.maxRetries;

    // Check if error is retryable
    if (isRetryableError(error, DEFAULT_RETRY_CONFIG) && originalRequest && retryCount < maxRetries) {
      // For non-idempotent methods (POST, PUT, DELETE, PATCH), only retry on specific errors
      const method = originalRequest.method?.toUpperCase();
      const isIdempotent = isIdempotentMethod(method);
      
      // For non-idempotent methods, only retry on network errors or 5xx errors (not 429)
      if (!isIdempotent && status && status !== 500 && status !== 502 && status !== 503 && status !== 504) {
        // Don't retry non-idempotent methods on client errors (except 5xx)
        const apiError = parseApiError(error);
        return Promise.reject(apiError);
      }

      // Mark that we're retrying this request
      (originalRequest as any)._retryCount = retryCount + 1;

      // Calculate delay (respect Retry-After header if present, otherwise exponential backoff with jitter)
      const delay = getRetryDelay(error, retryCount, DEFAULT_RETRY_CONFIG.baseDelay, DEFAULT_RETRY_CONFIG.maxDelay);

      // Log retry attempt with request_id if available
      const apiError = parseApiError(error);
      const errorType = status ? `HTTP ${status}` : error.code || 'Network Error';
      
      if (apiError.request_id) {
        console.warn(
          `[API Retry] ${errorType} error, retrying (${retryCount + 1}/${maxRetries}) - Request ID: ${apiError.request_id}`,
          {
            status: status || 'N/A',
            error_code: error.code || 'N/A',
            retry_count: retryCount + 1,
            max_retries: maxRetries,
            delay_ms: Math.round(delay),
            request_id: apiError.request_id,
            url: originalRequest?.url,
            method: originalRequest?.method,
            is_idempotent: isIdempotent,
          },
        );
      } else {
        console.warn(
          `[API Retry] ${errorType} error, retrying (${retryCount + 1}/${maxRetries})`,
          {
            status: status || 'N/A',
            error_code: error.code || 'N/A',
            retry_count: retryCount + 1,
            max_retries: maxRetries,
            delay_ms: Math.round(delay),
            url: originalRequest?.url,
            method: originalRequest?.method,
            is_idempotent: isIdempotent,
          },
        );
      }

      // Wait before retrying
      return sleep(delay).then(() => {
        // Retry the request
        return apiClient(originalRequest);
      });
    }

    // If already retried maxRetries times or error is not retryable, reject immediately
    if (retryCount >= maxRetries) {
      const apiError = parseApiError(error);
      const errorType = status ? `HTTP ${status}` : error.code || 'Network Error';
      
      // Log final error with request_id after all retries failed
      if (apiError.request_id) {
        console.error(
          `[API Error] ${errorType} error after ${maxRetries} retries - Request ID: ${apiError.request_id}`,
          {
            code: apiError.code,
            message: apiError.message,
            request_id: apiError.request_id,
            timestamp: apiError.timestamp,
            url: originalRequest?.url,
            method: originalRequest?.method,
          },
        );
      } else {
        console.error(
          `[API Error] ${errorType} error after ${maxRetries} retries`,
          {
            code: apiError.code,
            message: apiError.message,
            timestamp: apiError.timestamp,
            url: originalRequest?.url,
            method: originalRequest?.method,
          },
        );
      }
      
      return Promise.reject(apiError);
    }

    // Parser l'erreur en ApiError standardisé pour les autres codes
    const apiError = parseApiError(error);

    // FE-COMP-005: Show toast notification for API errors (unless disabled)
    const shouldShowToast = !(originalRequest as any)?._disableToast && 
                            status !== 401 && // Don't show toast for 401 (handled by refresh)
                            status !== 404 && // Don't show toast for 404 (handled by router)
                            !axios.isCancel(error); // Don't show toast for cancelled requests

    if (shouldShowToast && typeof window !== 'undefined') {
      // Get user-friendly error message
      let errorMessage = apiError.message;
      
      // Customize message based on status code
      if (status === 403) {
        errorMessage = "Vous n'avez pas les permissions nécessaires pour effectuer cette action";
      } else if (status === 422) {
        errorMessage = apiError.details?.[0]?.message || apiError.message || "Erreur de validation";
      } else if (status === 429) {
        errorMessage = "Trop de requêtes. Veuillez patienter quelques instants";
      } else if (status >= 500) {
        errorMessage = "Une erreur serveur s'est produite. Veuillez réessayer plus tard";
      } else if (!error.response) {
        // Check if it's a timeout error
        if (isTimeoutError(error)) {
          errorMessage = getTimeoutMessage('normal');
        } else {
          errorMessage = "Erreur de connexion. Vérifiez votre connexion internet";
        }
        
        // FE-API-015: Queue request for offline replay if it's a network error
        if (originalRequest && offlineQueue.shouldQueueRequest(originalRequest)) {
          const isOffline = typeof navigator !== 'undefined' && !navigator.onLine;
          if (isOffline || (!error.response && error.request)) {
            // Determine priority based on request type
            const method = originalRequest.method?.toUpperCase();
            const priority = method === 'DELETE' ? 'low' : method === 'POST' ? 'high' : 'normal';
            
            try {
              await offlineQueue.queueRequest(originalRequest, { priority });
              // Show info toast that request was queued
              toast.success('Requête mise en file d\'attente. Elle sera envoyée à la reconnexion.', {
                duration: 4000,
              });
            } catch (queueError) {
              logger.error('[API] Failed to queue request for offline replay', { error: queueError });
            }
          }
        }
      }

      toast.error(errorMessage, {
        duration: status === 429 ? 8000 : 5000, // Longer duration for rate limit errors
      });
    }

    // Log error with request_id for correlation with backend logs
    if (apiError.request_id) {
      console.error(
        `[API Error] ${apiError.message} (Code: ${apiError.code}, Request ID: ${apiError.request_id})`,
        {
          code: apiError.code,
          message: apiError.message,
          request_id: apiError.request_id,
          timestamp: apiError.timestamp,
          details: apiError.details,
          context: apiError.context,
          url: originalRequest?.url,
          method: originalRequest?.method,
        },
      );
    } else {
      // Log without request_id if not available
      console.error(
        `[API Error] ${apiError.message} (Code: ${apiError.code})`,
        {
          code: apiError.code,
          message: apiError.message,
          timestamp: apiError.timestamp,
          details: apiError.details,
          url: originalRequest?.url,
          method: originalRequest?.method,
        },
      );
    }

    return Promise.reject(apiError);
  },
);

/**
 * FE-COMP-005: Get default success message based on HTTP method
 */
function getDefaultSuccessMessage(method: string): string {
  switch (method) {
    case 'POST':
      return 'Opération réussie';
    case 'PUT':
    case 'PATCH':
      return 'Modification réussie';
    case 'DELETE':
      return 'Suppression réussie';
    default:
      return '';
  }
}

/**
 * Helper function to create a cancellable request
 * Returns an object with the request promise and an abort function
 * 
 * @example
 * ```typescript
 * const { request, abort } = createCancellableRequest((signal) => 
 *   apiClient.get('/api/v1/tracks', { signal })
 * );
 * 
 * // Later, to cancel:
 * abort();
 * ```
 */
export function createCancellableRequest<T>(
  requestFn: (signal: AbortSignal) => Promise<T>,
): { request: Promise<T>; abort: () => void } {
  const abortController = new AbortController();
  const signal = abortController.signal;

  const request = requestFn(signal);

  return {
    request,
    abort: () => {
      abortController.abort();
    },
  };
}

/**
 * Helper function to create a request with timeout
 * Automatically cancels the request if it exceeds the timeout duration
 * 
 * @example
 * ```typescript
 * const { request, abort } = createRequestWithTimeout(
 *   (signal) => apiClient.get('/api/v1/tracks', { signal }),
 *   5000 // 5 seconds timeout
 * );
 * ```
 */
export function createRequestWithTimeout<T>(
  requestFn: (signal: AbortSignal) => Promise<T>,
  timeoutMs: number,
): { request: Promise<T>; abort: () => void } {
  const abortController = new AbortController();
  const signal = abortController.signal;

  // Set up timeout
  const timeoutId = setTimeout(() => {
    abortController.abort();
  }, timeoutMs);

  const request = requestFn(signal)
    .finally(() => {
      // Clear timeout if request completes before timeout
      clearTimeout(timeoutId);
    });

  return {
    request,
    abort: () => {
      clearTimeout(timeoutId);
      abortController.abort();
    },
  };
}

/**
 * FE-API-016: Enhanced API client methods with automatic deduplication
 * FE-API-017: Enhanced with response caching for GET requests
 * These methods automatically deduplicate identical concurrent requests and cache GET responses
 * 
 * @example
 * ```typescript
 * // Multiple identical requests will share the same promise
 * const promise1 = deduplicatedApiClient.get('/tracks');
 * const promise2 = deduplicatedApiClient.get('/tracks');
 * // promise1 === promise2 (same promise instance)
 * 
 * // Cached responses are returned immediately
 * const response1 = await deduplicatedApiClient.get('/tracks');
 * const response2 = await deduplicatedApiClient.get('/tracks'); // Returns from cache
 * ```
 */
export const deduplicatedApiClient = {
  get: <T = any>(url: string, config?: InternalAxiosRequestConfig) => {
    // FE-API-017: Check cache first
    if (!(config as any)?._disableCache) {
      const cachedResponse = responseCache.get({ ...config, method: 'GET', url });
      if (cachedResponse) {
        logger.debug(`[API] Using cached response for: ${url}`);
        return Promise.resolve(cachedResponse as AxiosResponse<T>);
      }
    }

    return requestDeduplication.getOrCreateRequest(
      { ...config, method: 'GET', url },
      () => apiClient.get<T>(url, config),
    );
  },
  
  post: <T = any>(url: string, data?: any, config?: InternalAxiosRequestConfig) => {
    return requestDeduplication.getOrCreateRequest(
      { ...config, method: 'POST', url, data },
      () => apiClient.post<T>(url, data, config),
    );
  },
  
  put: <T = any>(url: string, data?: any, config?: InternalAxiosRequestConfig) => {
    return requestDeduplication.getOrCreateRequest(
      { ...config, method: 'PUT', url, data },
      () => apiClient.put<T>(url, data, config),
    );
  },
  
  patch: <T = any>(url: string, data?: any, config?: InternalAxiosRequestConfig) => {
    return requestDeduplication.getOrCreateRequest(
      { ...config, method: 'PATCH', url, data },
      () => apiClient.patch<T>(url, data, config),
    );
  },
  
  delete: <T = any>(url: string, config?: InternalAxiosRequestConfig) => {
    return requestDeduplication.getOrCreateRequest(
      { ...config, method: 'DELETE', url },
      () => apiClient.delete<T>(url, config),
    );
  },
};
