/**
 * Timeout Handler Utility
 * FE-API-014: Request timeout handling with user feedback
 *
 * Provides timeout management with progressive user feedback for slow requests
 */

// CRITICAL FIX: Utiliser le wrapper lazy pour éviter les collisions de noms de variables
import toast from '@/utils/toast';
import { ERROR_MESSAGES } from './errorMessages';

/**
 * Timeout configuration for different request types
 */
export const TIMEOUT_CONFIG = {
  // Default timeout (matches apiClient default)
  default: 10000, // 10 seconds

  // Fast operations (should be quick)
  fast: 5000, // 5 seconds

  // Normal operations
  normal: 10000, // 10 seconds

  // Slow operations (uploads, processing)
  slow: 30000, // 30 seconds

  // Very slow operations (large uploads, batch operations)
  verySlow: 60000, // 60 seconds
} as const;

/**
 * Warning thresholds for showing user feedback
 * These are percentages of the timeout duration
 */
export const WARNING_THRESHOLDS = {
  // Show warning at 50% of timeout
  warning: 0.5,
  // Show critical warning at 80% of timeout
  critical: 0.8,
} as const;

/**
 * Timeout warning messages
 */
export const TIMEOUT_MESSAGES = {
  warning: 'La requête prend plus de temps que prévu...',
  critical: 'La requête est très lente. Vérifiez votre connexion.',
  timeout: ERROR_MESSAGES.TIMEOUT,
} as const;

/**
 * Options for timeout handling
 */
export interface TimeoutOptions {
  /** Timeout duration in milliseconds */
  timeout?: number;
  /** Whether to show warning toasts */
  showWarnings?: boolean;
  /** Custom warning message */
  warningMessage?: string;
  /** Custom critical warning message */
  criticalMessage?: string;
  /** Custom timeout message */
  timeoutMessage?: string;
  /** Callback when warning threshold is reached */
  onWarning?: () => void;
  /** Callback when critical threshold is reached */
  onCritical?: () => void;
  /** Callback when timeout occurs */
  onTimeout?: () => void;
}

/**
 * Creates a promise that rejects after the specified timeout
 * @param timeoutMs Timeout duration in milliseconds
 * @param message Error message for timeout
 * @returns Promise that rejects with a timeout error
 */
export function createTimeoutPromise(
  timeoutMs: number,
  message: string = TIMEOUT_MESSAGES.timeout,
): Promise<never> {
  return new Promise((_, reject) => {
    setTimeout(() => {
      reject(new Error(message));
    }, timeoutMs);
  });
}

/**
 * Wraps a promise with timeout handling and progressive warnings
 * @param promise The promise to wrap
 * @param options Timeout options
 * @returns Promise that resolves/rejects with the original promise or timeout
 */
export function withTimeout<T>(
  promise: Promise<T>,
  options: TimeoutOptions = {},
): Promise<T> {
  const {
    timeout = TIMEOUT_CONFIG.default,
    showWarnings = true,
    warningMessage = TIMEOUT_MESSAGES.warning,
    criticalMessage = TIMEOUT_MESSAGES.critical,
    timeoutMessage = TIMEOUT_MESSAGES.timeout,
    onWarning,
    onCritical,
    onTimeout,
  } = options;

  let warningShown = false;
  let criticalShown = false;
  let warningToastId: string | undefined;
  let criticalToastId: string | undefined;

  // Calculate warning times
  const warningTime = timeout * WARNING_THRESHOLDS.warning;
  const criticalTime = timeout * WARNING_THRESHOLDS.critical;

  // Set up warning timers
  const warningTimer = setTimeout(() => {
    if (showWarnings && !warningShown) {
      warningShown = true;
      warningToastId = toast.loading(warningMessage, {
        duration: timeout - warningTime, // Show until timeout or completion
      });
      onWarning?.();
    }
  }, warningTime);

  const criticalTimer = setTimeout(() => {
    if (showWarnings && !criticalShown) {
      criticalShown = true;
      // Dismiss warning toast if shown
      if (warningToastId) {
        toast.dismiss(warningToastId);
      }
      criticalToastId = toast.loading(criticalMessage, {
        duration: timeout - criticalTime, // Show until timeout or completion
      });
      onCritical?.();
    }
  }, criticalTime);

  // Create timeout promise
  const timeoutPromise = createTimeoutPromise(timeout, timeoutMessage);

  // Race between the original promise and timeout
  return Promise.race([promise, timeoutPromise])
    .then((result) => {
      // Clear timers if promise resolves before timeout
      clearTimeout(warningTimer);
      clearTimeout(criticalTimer);

      // Dismiss any active toasts
      if (warningToastId) {
        toast.dismiss(warningToastId);
      }
      if (criticalToastId) {
        toast.dismiss(criticalToastId);
      }

      return result;
    })
    .catch((error) => {
      // Clear timers
      clearTimeout(warningTimer);
      clearTimeout(criticalTimer);

      // Dismiss any active toasts
      if (warningToastId) {
        toast.dismiss(warningToastId);
      }
      if (criticalToastId) {
        toast.dismiss(criticalToastId);
      }

      // Call timeout callback if it's a timeout error
      if (error.message === timeoutMessage) {
        onTimeout?.();
      }

      throw error;
    });
}

/**
 * Gets appropriate timeout for a request type
 * @param requestType Type of request (fast, normal, slow, verySlow)
 * @returns Timeout duration in milliseconds
 */
export function getTimeoutForRequestType(
  requestType: keyof typeof TIMEOUT_CONFIG = 'normal',
): number {
  return TIMEOUT_CONFIG[requestType];
}

/**
 * Checks if an error is a timeout error
 * @param error Error to check
 * @returns True if error is a timeout error
 */
export function isTimeoutError(error: unknown): boolean {
  if (error instanceof Error) {
    return (
      error.message === TIMEOUT_MESSAGES.timeout ||
      error.message.includes('timeout') ||
      error.message.includes('expired') ||
      error.name === 'TimeoutError'
    );
  }

  // Check for Axios timeout errors
  if (error && typeof error === 'object' && 'code' in error) {
    const code = (error as any).code;
    return code === 'ECONNABORTED' || code === 'ETIMEDOUT';
  }

  return false;
}

/**
 * Gets user-friendly timeout message based on request type
 * @param requestType Type of request
 * @returns User-friendly timeout message
 */
export function getTimeoutMessage(
  requestType: keyof typeof TIMEOUT_CONFIG = 'normal',
): string {
  const messages: Record<keyof typeof TIMEOUT_CONFIG, string> = {
    default: 'La requête a expiré. Veuillez réessayer.',
    fast: 'La requête a expiré. Vérifiez votre connexion et réessayez.',
    normal: 'La requête a expiré. Veuillez réessayer.',
    slow: "L'opération prend plus de temps que prévu. Veuillez patienter ou réessayer plus tard.",
    verySlow:
      "L'opération est en cours. Cela peut prendre plusieurs minutes. Veuillez patienter.",
  };

  return messages[requestType] || messages.default;
}

/**
 * Wraps an API call with timeout and warning feedback
 * @param apiCall The API call function
 * @param requestType Type of request for timeout configuration
 * @param options Additional timeout options
 * @returns Promise with timeout handling
 */
export function withRequestTimeout<T>(
  apiCall: () => Promise<T>,
  requestType: keyof typeof TIMEOUT_CONFIG = 'normal',
  options: Omit<TimeoutOptions, 'timeout'> = {},
): Promise<T> {
  const timeout = getTimeoutForRequestType(requestType);
  const timeoutMessage = getTimeoutMessage(requestType);

  return withTimeout(apiCall(), {
    ...options,
    timeout,
    timeoutMessage,
  });
}
