import { apiClient, API_TIMEOUTS } from '@/services/api/client';
import { Track, UploadProgress } from '../types/track';
import { AxiosError, AxiosProgressEvent } from 'axios';
import { TrackServiceError as TrackUploadError } from '../errors/trackErrors';

/**
 * Upload un fichier audio
 * @param file Fichier audio à uploader
 * @param onProgress Callback optionnel pour suivre la progression de l'upload
 * @returns Le track créé
 * @throws Error si la requête échoue
 */
export async function uploadTrack(
  file: File,
  onProgress?: (progress: number) => void,
): Promise<Track> {
  try {
    const formData = new FormData();
    formData.append('file', file);

    const response = await apiClient.post<{ track: Track }>(
      '/tracks',
      formData,
      {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
        onUploadProgress: (progressEvent: AxiosProgressEvent) => {
          if (progressEvent.total && onProgress) {
            const progress = Math.round(
              (progressEvent.loaded * 100) / progressEvent.total,
            );
            onProgress(progress);
          }
        },
        timeout: API_TIMEOUTS.UPLOAD, // INT-API-004: Use standardized upload timeout (5 minutes)
      },
    );

    return response.data.track;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      // Gérer les erreurs spécifiques selon le code de statut HTTP
      if (error.response?.status === 400) {
        const errorMessage =
          error.response?.data?.error || 'Format ou taille invalide';
        throw new TrackUploadError(errorMessage, 'VALIDATION', false, error);
      }
      // ... (keep existing error handling)
      // ...
    }
    // Erreur inconnue
    throw new TrackUploadError(
      "Erreur inconnue lors de l'upload",
      'UNKNOWN',
      false,
      error,
    );
  }
}

/**
 * Initialise un upload par chunks
 * @param totalChunks Nombre total de chunks
 * @param totalSize Taille totale du fichier
 * @param filename Nom du fichier
 * @returns ID de l'upload
 */
export async function initiateChunkedUpload(
  totalChunks: number,
  totalSize: number,
  filename: string,
): Promise<string> {
  const response = await apiClient.post<{ upload_id: string }>(
    '/tracks/initiate',
    {
      total_chunks: totalChunks,
      total_size: totalSize,
      filename,
    },
  );
  return response.data.upload_id;
}

/**
 * Interface pour la réponse d'upload d'un chunk
 */
export interface UploadChunkResponse {
  message: string;
  upload_id: string;
  received_chunks: number;
  total_chunks: number;
  progress: number;
}

/**
 * Upload un chunk
 * @param uploadId ID de l'upload
 * @param chunkNumber Numéro du chunk (1-based)
 * @param totalChunks Nombre total de chunks
 * @param totalSize Taille totale du fichier
 * @param filename Nom du fichier
 * @param chunk Blob du chunk
 * @param onProgress Callback de progression
 * @returns Réponse avec la progression de l'upload
 */
export async function uploadChunk(
  uploadId: string,
  chunkNumber: number,
  totalChunks: number,
  totalSize: number,
  filename: string,
  chunk: Blob,
  onProgress?: (progress: number) => void,
): Promise<UploadChunkResponse> {
  const formData = new FormData();
  formData.append('upload_id', uploadId);
  formData.append('chunk_number', chunkNumber.toString());
  formData.append('total_chunks', totalChunks.toString());
  formData.append('total_size', totalSize.toString());
  formData.append('filename', filename);
  formData.append('chunk', chunk);

  const response = await apiClient.post<UploadChunkResponse>(
    '/tracks/chunk',
    formData,
    {
      headers: {
        'Content-Type': 'multipart/form-data',
      },
      onUploadProgress: (progressEvent: AxiosProgressEvent) => {
        if (progressEvent.total && onProgress) {
          const progress = Math.round(
            (progressEvent.loaded * 100) / progressEvent.total,
          );
          onProgress(progress);
        }
      },
    },
  );
  return response.data;
}

/**
 * Complète un upload par chunks
 * @param uploadId ID de l'upload
 * @returns Le track créé
 */
export async function completeChunkedUpload(uploadId: string): Promise<Track> {
  const response = await apiClient.post<{ track: Track }>('/tracks/complete', {
    upload_id: uploadId,
  });
  return response.data.track;
}

/**
 * Récupère la progression d'un upload de track
 * @param trackId ID du track
 * @returns Les informations de progression
 * @throws Error si la requête échoue
 */
export async function getUploadProgress(
  trackId: string,
): Promise<UploadProgress> {
  try {
    const response = await apiClient.get<{ progress: UploadProgress }>(
      `/tracks/${trackId}/status`,
    );
    return response.data.progress;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      if (error.response?.status === 401) {
        throw new TrackUploadError(
          'Non autorisé: Veuillez vous connecter pour voir la progression',
          'VALIDATION',
          false,
          error,
        );
      }
      if (error.response?.status === 404) {
        throw new TrackUploadError(
          'Track introuvable',
          'VALIDATION',
          false,
          error,
        );
      }
      if (error.response?.status === 500) {
        throw new TrackUploadError(
          'Erreur serveur: Impossible de récupérer la progression. Veuillez réessayer plus tard.',
          'SERVER',
          true,
          error,
        );
      }
      // Erreurs réseau
      if (
        error.code === 'ECONNABORTED' ||
        error.code === 'ETIMEDOUT' ||
        !error.response
      ) {
        throw new TrackUploadError(
          'Erreur réseau: Impossible de se connecter au serveur.',
          'NETWORK',
          true,
          error,
        );
      }
      const errorMessage =
        error.response?.data?.error ||
        error.message ||
        'Échec de la récupération de la progression';
      throw new TrackUploadError(errorMessage, 'UNKNOWN', false, error);
    }
    if (error instanceof TrackUploadError) {
      throw error;
    }
    throw new TrackUploadError(
      'Erreur inconnue lors de la récupération de la progression',
      'UNKNOWN',
      false,
      error,
    );
  }
}

/**
 * Interface pour les informations de quota d'un utilisateur
 */
export interface UserQuota {
  tracks_count: number;
  tracks_limit: number;
  storage_used: number; // bytes
  storage_limit: number; // bytes
}

/**
 * Récupère les informations de quota d'upload pour un utilisateur
 * @param userId ID de l'utilisateur (ou "me" pour l'utilisateur authentifié)
 * @returns Les informations de quota
 * @throws Error si la requête échoue
 */
export async function getUserQuota(userId: string): Promise<UserQuota> {
  try {
    const userIdParam =
      userId === 'me' || userId === 'current' ? 'me' : String(userId);
    const response = await apiClient.get<{ quota: UserQuota }>(
      `/users/${userIdParam}/upload-quota`,
    );
    return response.data.quota;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      if (error.response?.status === 401) {
        throw new TrackUploadError(
          'Non autorisé: Veuillez vous connecter pour voir votre quota',
          'VALIDATION',
          false,
          error,
        );
      }
      if (error.response?.status === 403) {
        throw new TrackUploadError(
          'Accès refusé: Vous ne pouvez voir que votre propre quota',
          'VALIDATION',
          false,
          error,
        );
      }
      if (error.response?.status === 404) {
        throw new TrackUploadError(
          'Utilisateur introuvable',
          'VALIDATION',
          false,
          error,
        );
      }
      if (error.response?.status === 500) {
        throw new TrackUploadError(
          'Erreur serveur: Impossible de récupérer le quota. Veuillez réessayer plus tard.',
          'SERVER',
          true,
          error,
        );
      }
      // Erreurs réseau
      if (
        error.code === 'ECONNABORTED' ||
        error.code === 'ETIMEDOUT' ||
        !error.response
      ) {
        throw new TrackUploadError(
          'Erreur réseau: Impossible de se connecter au serveur.',
          'NETWORK',
          true,
          error,
        );
      }
      const errorMessage =
        error.response?.data?.error ||
        error.message ||
        'Échec de la récupération du quota';
      throw new TrackUploadError(errorMessage, 'UNKNOWN', false, error);
    }
    if (error instanceof TrackUploadError) {
      throw error;
    }
    throw new TrackUploadError(
      'Erreur inconnue lors de la récupération du quota',
      'UNKNOWN',
      false,
      error,
    );
  }
}

/**
 * Interface pour l'état d'un upload résumable
 */
export interface UploadResumeState {
  upload_id: string;
  user_id: string;
  total_chunks: number;
  total_size: number;
  filename: string;
  chunks_received: number[];
  received_count: number;
  last_chunk: number;
  progress: number;
  created_at: string;
  updated_at: string;
}

/**
 * Récupère l'état d'un upload pour permettre la reprise
 * @param uploadId ID de l'upload
 * @returns L'état de l'upload
 * @throws Error si la requête échoue
 */
export async function resumeUpload(
  uploadId: string,
): Promise<UploadResumeState> {
  try {
    const response = await apiClient.get<UploadResumeState>(
      `/tracks/upload/resume/${uploadId}`,
    );
    return response.data;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      if (error.response?.status === 401) {
        throw new TrackUploadError(
          "Non autorisé: Veuillez vous connecter pour reprendre l'upload",
          'VALIDATION',
          false,
          error,
        );
      }
      if (error.response?.status === 403) {
        throw new TrackUploadError(
          'Accès refusé: Vous ne pouvez reprendre que vos propres uploads',
          'VALIDATION',
          false,
          error,
        );
      }
      if (error.response?.status === 404) {
        throw new TrackUploadError(
          'Upload introuvable ou expiré',
          'VALIDATION',
          false,
          error,
        );
      }
      if (error.response?.status === 500) {
        throw new TrackUploadError(
          "Erreur serveur: Impossible de récupérer l'état de l'upload. Veuillez réessayer plus tard.",
          'SERVER',
          true,
          error,
        );
      }
      // Erreurs réseau
      if (
        error.code === 'ECONNABORTED' ||
        error.code === 'ETIMEDOUT' ||
        !error.response
      ) {
        throw new TrackUploadError(
          'Erreur réseau: Impossible de se connecter au serveur.',
          'NETWORK',
          true,
          error,
        );
      }
      const errorMessage =
        error.response?.data?.error ||
        error.message ||
        "Échec de la récupération de l'état de l'upload";
      throw new TrackUploadError(errorMessage, 'UNKNOWN', false, error);
    }
    if (error instanceof TrackUploadError) {
      throw error;
    }
    throw new TrackUploadError(
      "Erreur inconnue lors de la récupération de l'état de l'upload",
      'UNKNOWN',
      false,
      error,
    );
  }
}
