/**
 * Track API - Feature API Layer
 * Action 6.1.1.10: Update feature API files to use services
 * 
 * This file provides the implementation layer for track API operations.
 * The unified service layer (@/services/api/tracks) wraps functions from this file.
 * 
 * Components should use @/services/api/tracks (tracksApi) instead of importing
 * directly from this file for better abstraction and consistency.
 * 
 * This file remains as the implementation layer and is used by the service layer.
 */

import { apiClient, API_TIMEOUTS } from '@/services/api/client';
import { Track, TrackStatus } from '../types/track';
import { AxiosError, AxiosProgressEvent } from 'axios';

/**
 * Track API
 * API layer pour l'upload et la récupération de tracks
 * Utilise FormData pour l'upload et gère la pagination
 * 
 * @deprecated For new code, use @/services/api/tracks (tracksApi) instead
 */

export interface TrackMetadata {
  title?: string;
  artist?: string;
  album?: string;
  genre?: string;
  year?: number;
  is_public?: boolean;
}

export interface UpdateTrackRequest {
  title?: string;
  artist?: string;
  album?: string;
  genre?: string;
  year?: number;
  is_public?: boolean;
}

export interface TrackStats {
  // Structure à définir selon l'implémentation backend
  [key: string]: unknown;
}

export interface TrackHistory {
  // Structure à définir selon l'implémentation backend
  [key: string]: unknown;
}

export interface CreateShareRequest {
  permissions: 'read' | 'write' | 'admin';
  expires_at?: string;
}

export interface Share {
  id: string;
  track_id: string;
  token: string;
  permissions: string;
  expires_at?: string;
  created_at: string;
}

export interface TrackLikesResponse {
  count: number;
  is_liked: boolean;
}

export interface GetTracksParams {
  page?: number;
  limit?: number;
  userId?: string;
  genre?: string;
  format?: string;
  sortBy?: 'created_at' | 'title' | 'popularity';
  sortOrder?: 'asc' | 'desc';
  search?: string;
}

export interface GetTracksResponse {
  tracks: Track[];
  pagination: {
    page: number;
    limit: number;
    total: number;
    total_pages: number;
  };
}

/**
 * Interface pour la réponse 202 Accepted lors d'un upload async
 */
interface AsyncUploadResponse {
  track_id: string;
  status: TrackStatus;
  status_url: string;
  message: string;
}

/**
 * Poll le statut d'un track jusqu'à ce qu'il soit completed ou failed
 * @param trackId ID du track à poller
 * @param onProgress Callback optionnel pour suivre la progression
 * @returns Le track complet une fois terminé
 * @throws Error si le track échoue ou si le polling timeout
 */
async function pollTrackStatus(
  trackId: string,
  onProgress?: (progress: number) => void,
): Promise<Track> {
  const maxAttempts = 120; // 2 minutes max (1 seconde par tentative)
  const pollInterval = 1000; // 1 seconde entre chaque tentative
  let attempts = 0;

  while (attempts < maxAttempts) {
    try {
      // Récupérer le track pour vérifier son status
      const response = await apiClient.get<Track>(`/tracks/${trackId}`);
      const track = response.data;

      // Mettre à jour la progression si callback fourni
      if (onProgress) {
        // Estimer la progression basée sur le status
        let progress = 0;
        if (track.status === 'uploading') {
          progress = 30; // Upload en cours
        } else if (track.status === 'processing') {
          progress = 70; // Traitement en cours
        } else if (track.status === 'completed') {
          progress = 100; // Terminé
        }
        onProgress(progress);
      }

      // Si le track est terminé (completed ou failed), le retourner
      if (track.status === 'completed') {
        return track;
      }
      if (track.status === 'failed') {
        throw new Error(
          track.status_message || 'Le traitement du fichier a échoué',
        );
      }

      // Attendre avant le prochain poll
      await new Promise((resolve) => setTimeout(resolve, pollInterval));
      attempts++;
    } catch (error: unknown) {
      // Si c'est une erreur 404, le track n'existe pas encore, continuer à poller
      if (error instanceof AxiosError && error.response?.status === 404) {
        await new Promise((resolve) => setTimeout(resolve, pollInterval));
        attempts++;
        continue;
      }
      // Sinon, propager l'erreur
      throw error;
    }
  }

  // Timeout après maxAttempts
  throw new Error(
    'Le traitement du fichier prend plus de temps que prévu. Veuillez réessayer plus tard.',
  );
}

/**
 * Upload un fichier audio avec métadonnées
 * @param file Fichier audio à uploader
 * @param metadata Métadonnées optionnelles du track
 * @param onProgress Callback optionnel pour suivre la progression
 * @returns Le track créé
 * @throws Error si la requête échoue
 */
export async function uploadTrack(
  file: File,
  metadata: TrackMetadata = {},
  onProgress?: (progress: number) => void,
): Promise<Track> {
  try {
    const formData = new FormData();
    // Le backend attend un champ nommé 'file'
    formData.append('file', file);

    // Ajouter les métadonnées au FormData
    if (metadata.title) {
      formData.append('title', metadata.title);
    }
    if (metadata.artist) {
      formData.append('artist', metadata.artist);
    }
    if (metadata.album) {
      formData.append('album', metadata.album);
    }
    if (metadata.genre) {
      formData.append('genre', metadata.genre);
    }
    if (metadata.year) {
      formData.append('year', metadata.year.toString());
    }
    if (metadata.is_public !== undefined) {
      formData.append('is_public', metadata.is_public.toString());
    }

    // Construction de la config pour l'upload
    // NOTE: Le token est automatiquement ajouté par l'interceptor de apiClient
    // (voir apps/web/src/services/api/client.ts)
    // CRITIQUE: Ne pas définir Content-Type - Axios le calcule automatiquement
    // avec le boundary correct pour multipart/form-data
    const config = {
      onUploadProgress: (progressEvent: AxiosProgressEvent) => {
        if (progressEvent.total && onProgress) {
          // Pour l'upload initial, on limite à 20% (le reste sera géré par le polling)
          const uploadProgress = Math.round(
            (progressEvent.loaded * 100) / progressEvent.total,
          );
          onProgress(Math.min(uploadProgress * 0.2, 20));
        }
      },
      timeout: API_TIMEOUTS.UPLOAD, // INT-API-004: Use standardized upload timeout (5 minutes)
    };

    const response = await apiClient.post<Track | AsyncUploadResponse>(
      '/tracks',
      formData,
      config,
    );

    // Vérifier si c'est une réponse 202 Accepted (upload async)
    if (response.status === 202) {
      // Extraire le track_id de la réponse
      const asyncResponse = response.data as AsyncUploadResponse;
      const trackId = asyncResponse.track_id;

      // Mettre à jour la progression (upload terminé, traitement en cours)
      if (onProgress) {
        onProgress(25);
      }

      // Poller le statut jusqu'à completion
      return await pollTrackStatus(trackId, onProgress);
    }

    // Sinon, c'est une réponse 201 Created avec le Track complet
    return response.data as 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?.message ||
          error.response?.data?.message ||
          'Format ou taille invalide';
        throw new Error(errorMessage);
      }
      if (error.response?.status === 413) {
        throw new Error('Fichier trop volumineux');
      }
      if (error.response?.status === 415) {
        throw new Error('Format de fichier non supporté');
      }

      if (error.response?.status === 500) {
        throw new Error(
          'Erreur serveur: Impossible de traiter le fichier. Veuillez réessayer plus tard.',
        );
      }
      // Erreurs réseau
      if (
        error.code === 'ECONNABORTED' ||
        error.code === 'ETIMEDOUT' ||
        !error.response
      ) {
        throw new Error(
          'Erreur réseau: Impossible de se connecter au serveur.',
        );
      }
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        "Erreur lors de l'upload";
      throw new Error(errorMessage);
    }
    throw error;
  }
}

/**
 * Récupère la liste paginée des tracks
 * @param page Numéro de page (défaut: 1)
 * @param limit Nombre d'éléments par page (défaut: 20)
 * @param params Paramètres optionnels supplémentaires
 * @returns La liste des tracks avec les métadonnées de pagination
 * @throws Error si la requête échoue
 */
export async function getTracks(
  page: number = 1,
  limit: number = 20,
  params?: Omit<GetTracksParams, 'page' | 'limit'>,
): Promise<GetTracksResponse> {
  try {
    const queryParams = new URLSearchParams();
    queryParams.append('page', page.toString());
    queryParams.append('limit', limit.toString());

    if (params?.userId !== undefined) {
      queryParams.append('user_id', params.userId.toString());
    }
    if (params?.genre) {
      queryParams.append('genre', params.genre);
    }
    if (params?.format) {
      queryParams.append('format', params.format);
    }
    // CRITIQUE FIX #48: Ajouter le paramètre de recherche au backend
    if (params?.search) {
      queryParams.append('search', params.search);
    }
    if (params?.sortBy) {
      queryParams.append('sort_by', params.sortBy);
    }
    if (params?.sortOrder) {
      queryParams.append('sort_order', params.sortOrder);
    }

    const queryString = queryParams.toString();
    const url = `/tracks${queryString ? `?${queryString}` : ''}`;

    const response = await apiClient.get<GetTracksResponse>(url);
    return response.data;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      if (error.response?.status === 500) {
        throw new Error(
          'Erreur serveur: Impossible de récupérer les tracks. Veuillez réessayer plus tard.',
        );
      }
      // Erreurs réseau
      if (
        error.code === 'ECONNABORTED' ||
        error.code === 'ETIMEDOUT' ||
        !error.response
      ) {
        throw new Error(
          'Erreur réseau: Impossible de se connecter au serveur.',
        );
      }
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        'Échec de la récupération des tracks';
      throw new Error(errorMessage);
    }
    throw error;
  }
}

/**
 * Met à jour les métadonnées d'un track
 * @param trackId ID du track à mettre à jour
 * @param updates Métadonnées à mettre à jour
 * @returns Le track mis à jour
 * @throws Error si la requête échoue
 */
export async function updateTrack(
  trackId: string,
  updates: UpdateTrackRequest,
): Promise<Track> {
  try {
    const { data } = await apiClient.put<{ track: Track }>(
      `/tracks/${trackId}`,
      updates,
    );
    return data.track;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      if (error.response?.status === 404) {
        throw new Error('Track introuvable');
      }
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        'Échec de la mise à jour du track';
      throw new Error(errorMessage);
    }
    throw error;
  }
}

/**
 * Récupère les statistiques d'un track
 * @param trackId ID du track
 * @returns Les statistiques du track
 * @throws Error si la requête échoue
 */
export async function getTrackStats(trackId: string): Promise<TrackStats> {
  try {
    const { data } = await apiClient.get<{ stats: TrackStats }>(
      `/tracks/${trackId}/stats`,
    );
    return data.stats;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      if (error.response?.status === 404) {
        throw new Error('Track introuvable');
      }
      if (error.response?.status === 501) {
        throw new Error('Statistiques non disponibles pour le moment');
      }
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        'Échec de la récupération des statistiques';
      throw new Error(errorMessage);
    }
    throw error;
  }
}

/**
 * Récupère l'historique d'un track
 * @param trackId ID du track
 * @returns L'historique du track
 * @throws Error si la requête échoue
 */
export async function getTrackHistory(trackId: string): Promise<TrackHistory> {
  try {
    const { data } = await apiClient.get<{ history: TrackHistory }>(
      `/tracks/${trackId}/history`,
    );
    return data.history;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      if (error.response?.status === 404) {
        throw new Error('Track introuvable');
      }
      if (error.response?.status === 501) {
        throw new Error('Historique non disponible pour le moment');
      }
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        "Échec de la récupération de l'historique";
      throw new Error(errorMessage);
    }
    throw error;
  }
}

/**
 * Télécharge un track
 * @param trackId ID du track à télécharger
 * @param shareToken Token de partage optionnel
 * @returns Le blob du fichier audio
 * @throws Error si la requête échoue
 */
export async function downloadTrack(
  trackId: string,
  shareToken?: string,
): Promise<Blob> {
  try {
    const config = shareToken
      ? { params: { share_token: shareToken }, responseType: 'blob' as const }
      : { responseType: 'blob' as const };
    const response = await apiClient.get(`/tracks/${trackId}/download`, config);
    return response.data;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      if (error.response?.status === 404) {
        throw new Error('Track introuvable');
      }
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        'Échec du téléchargement';
      throw new Error(errorMessage);
    }
    throw error;
  }
}

/**
 * Like un track
 * @param trackId ID du track à liker
 * @throws Error si la requête échoue
 */
export async function likeTrack(trackId: string): Promise<void> {
  try {
    await apiClient.post(`/tracks/${trackId}/like`);
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      if (error.response?.status === 404) {
        throw new Error('Track introuvable');
      }
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        'Échec du like';
      throw new Error(errorMessage);
    }
    throw error;
  }
}

/**
 * Unlike un track
 * @param trackId ID du track à unliker
 * @throws Error si la requête échoue
 */
export async function unlikeTrack(trackId: string): Promise<void> {
  try {
    await apiClient.delete(`/tracks/${trackId}/like`);
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      if (error.response?.status === 404) {
        throw new Error('Track introuvable');
      }
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        'Échec du unlike';
      throw new Error(errorMessage);
    }
    throw error;
  }
}

/**
 * Supprime un track
 * @param trackId ID du track à supprimer
 * @throws Error si la requête échoue
 */
export async function deleteTrack(trackId: string): Promise<void> {
  try {
    await apiClient.delete(`/tracks/${trackId}`);
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      if (error.response?.status === 404) {
        throw new Error('Track introuvable');
      }
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        'Échec de la suppression du track';
      throw new Error(errorMessage);
    }
    throw error;
  }
}

/**
 * Récupère les likes d'un track
 * @param trackId ID du track
 * @returns Le nombre de likes et si l'utilisateur actuel a liké
 * @throws Error si la requête échoue
 */
export async function getTrackLikes(
  trackId: string,
): Promise<TrackLikesResponse> {
  try {
    const { data } = await apiClient.get<TrackLikesResponse>(
      `/tracks/${trackId}/likes`,
    );
    return data;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      if (error.response?.status === 404) {
        throw new Error('Track introuvable');
      }
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        'Échec de la récupération des likes';
      throw new Error(errorMessage);
    }
    throw error;
  }
}

/**
 * Crée un lien de partage pour un track
 * @param trackId ID du track à partager
 * @param request Paramètres de partage (permissions, expires_at)
 * @returns Le lien de partage créé
 * @throws Error si la requête échoue
 */
export async function createTrackShare(
  trackId: string,
  request: CreateShareRequest,
): Promise<Share> {
  try {
    const { data } = await apiClient.post<{ share: Share }>(
      `/tracks/${trackId}/share`,
      request,
    );
    return data.share;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      if (error.response?.status === 404) {
        throw new Error('Track introuvable');
      }
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        'Échec de la création du lien de partage';
      throw new Error(errorMessage);
    }
    throw error;
  }
}

// Interfaces pour le chunked upload
interface UploadChunkResponse {
  message: string;
  upload_id: string;
  received_chunks: number;
  total_chunks: number;
  progress: number;
}

interface InitiateUploadResponse {
  upload_id: string;
  message: string;
}

interface CompleteUploadResponse {
  message: string;
  track: Track;
  md5: string;
}

/**
 * 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> {
  try {
    const { data } = await apiClient.post<InitiateUploadResponse>(
      '/tracks/initiate',
      {
        total_chunks: totalChunks,
        total_size: totalSize,
        filename,
      },
    );
    return data.upload_id;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        "Échec de l'initialisation de l'upload";
      throw new Error(errorMessage);
    }
    throw error;
  }
}

/**
 * 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 Données du chunk (Blob)
 * @param onProgress Callback de progression
 */
export async function uploadChunk(
  uploadId: string,
  chunkNumber: number,
  totalChunks: number,
  totalSize: number,
  filename: string,
  chunk: Blob,
  onProgress?: (progress: number) => void,
): Promise<UploadChunkResponse> {
  try {
    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);

    // CRITIQUE: Ne pas définir Content-Type pour multipart/form-data avec Axios
    const config = onProgress
      ? {
          onUploadProgress: (progressEvent: AxiosProgressEvent) => {
            if (progressEvent.total) {
              const progress = Math.round(
                (progressEvent.loaded * 100) / progressEvent.total,
              );
              onProgress(progress);
            }
          },
        }
      : {};

    const { data } = await apiClient.post<UploadChunkResponse>(
      '/tracks/chunk',
      formData,
      config,
    );
    return data;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        `Échec de l'upload du chunk ${chunkNumber}`;
      throw new Error(errorMessage);
    }
    throw error;
  }
}

/**
 * Termine un upload par chunks
 * @param uploadId ID de l'upload
 * @returns Le track créé
 */
export async function completeChunkedUpload(uploadId: string): Promise<Track> {
  try {
    const { data } = await apiClient.post<CompleteUploadResponse>(
      '/tracks/complete',
      { upload_id: uploadId },
    );
    return data.track;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        "Échec de la finalisation de l'upload";
      throw new Error(errorMessage);
    }
    throw error;
  }
}

/**
 * FE-PAGE-002: Batch operations interfaces
 */
export interface BatchDeleteRequest {
  track_ids: string[];
}

export interface BatchDeleteResponse {
  deleted_count: number;
  message: string;
}

export interface BatchUpdateRequest {
  track_ids: string[];
  updates: UpdateTrackRequest;
}

export interface BatchUpdateResponse {
  updated_count: number;
  message: string;
}

/**
 * Supprime plusieurs tracks en une seule requête
 * @param trackIds Liste des IDs de tracks à supprimer
 * @returns Nombre de tracks supprimés
 * @throws Error si la requête échoue
 */
export async function batchDeleteTracks(
  trackIds: string[],
): Promise<BatchDeleteResponse> {
  try {
    const { data } = await apiClient.post<BatchDeleteResponse>(
      '/tracks/batch/delete',
      { track_ids: trackIds },
    );
    return data;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        'Échec de la suppression en masse';
      throw new Error(errorMessage);
    }
    throw error;
  }
}

/**
 * Met à jour plusieurs tracks en une seule requête
 * @param trackIds Liste des IDs de tracks à mettre à jour
 * @param updates Métadonnées à appliquer à tous les tracks
 * @returns Nombre de tracks mis à jour
 * @throws Error si la requête échoue
 */
export async function batchUpdateTracks(
  trackIds: string[],
  updates: UpdateTrackRequest,
): Promise<BatchUpdateResponse> {
  try {
    const { data } = await apiClient.post<BatchUpdateResponse>(
      '/tracks/batch/update',
      {
        track_ids: trackIds,
        updates,
      },
    );
    return data;
  } catch (error: unknown) {
    if (error instanceof AxiosError) {
      const errorMessage =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message ||
        'Échec de la mise à jour en masse';
      throw new Error(errorMessage);
    }
    throw error;
  }
}
