import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
  searchTracks,
  TrackSearchError,
  type TrackSearchParams,
} from './trackSearchService';
import { apiClient } from '@/services/api/client';
import { AxiosError } from 'axios';
import type { Track } from '../types/track';

// Mock apiClient
vi.mock('@/services/api/client', () => ({
  apiClient: {
    get: vi.fn(),
  },
}));

describe('trackSearchService', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('searchTracks', () => {
    it('should search tracks with basic query', async () => {
      const mockTracks: Track[] = [
        {
          id: 1,
          creator_id: 123,
          title: 'Test Track',
          artist: 'Test Artist',
          duration: 180,
          file_path: '/test/track.mp3',
          file_size: 5000000,
          format: 'MP3',
          is_public: true,
          play_count: 10,
          like_count: 5,
          created_at: '2024-01-01T00:00:00Z',
          updated_at: '2024-01-01T00:00:00Z',
        },
      ];

      const mockResponse = {
        data: {
          tracks: mockTracks,
          pagination: {
            page: 1,
            limit: 20,
            total: 1,
            total_pages: 1,
          },
        },
      };

      vi.mocked(apiClient.get).mockResolvedValue(mockResponse);

      const result = await searchTracks({ query: 'Test' });

      expect(result.tracks).toEqual(mockTracks);
      expect(result.pagination.total).toBe(1);
      expect(apiClient.get).toHaveBeenCalledWith('/tracks/search?q=Test');
    });

    it('should search tracks with multiple filters', async () => {
      const mockTracks: Track[] = [];
      const mockResponse = {
        data: {
          tracks: mockTracks,
          pagination: {
            page: 1,
            limit: 20,
            total: 0,
            total_pages: 0,
          },
        },
      };

      vi.mocked(apiClient.get).mockResolvedValue(mockResponse);

      const params: TrackSearchParams = {
        query: 'Rock',
        genre: 'Rock',
        format: 'MP3',
        minDuration: 120,
        maxDuration: 300,
        page: 1,
        limit: 20,
        sortBy: 'popularity',
        sortOrder: 'desc',
      };

      await searchTracks(params);

      expect(apiClient.get).toHaveBeenCalledWith(
        '/tracks/search?q=Rock&genre=Rock&format=MP3&min_duration=120&max_duration=300&page=1&limit=20&sort_by=popularity&sort_order=desc',
      );
    });

    it('should search tracks with tags', async () => {
      const mockResponse = {
        data: {
          tracks: [],
          pagination: {
            page: 1,
            limit: 20,
            total: 0,
            total_pages: 0,
          },
        },
      };

      vi.mocked(apiClient.get).mockResolvedValue(mockResponse);

      await searchTracks({
        tags: ['rock', 'pop'],
        tagMode: 'OR',
      });

      expect(apiClient.get).toHaveBeenCalledWith(
        '/tracks/search?tags=rock%2Cpop&tag_mode=OR',
      );
    });

    it('should search tracks with date range', async () => {
      const mockResponse = {
        data: {
          tracks: [],
          pagination: {
            page: 1,
            limit: 20,
            total: 0,
            total_pages: 0,
          },
        },
      };

      vi.mocked(apiClient.get).mockResolvedValue(mockResponse);

      await searchTracks({
        minDate: '2024-01-01T00:00:00Z',
        maxDate: '2024-12-31T23:59:59Z',
      });

      expect(apiClient.get).toHaveBeenCalledWith(
        '/tracks/search?min_date=2024-01-01T00%3A00%3A00Z&max_date=2024-12-31T23%3A59%3A59Z',
      );
    });

    it('should search tracks with BPM range', async () => {
      const mockResponse = {
        data: {
          tracks: [],
          pagination: {
            page: 1,
            limit: 20,
            total: 0,
            total_pages: 0,
          },
        },
      };

      vi.mocked(apiClient.get).mockResolvedValue(mockResponse);

      await searchTracks({
        minBPM: 120,
        maxBPM: 140,
      });

      expect(apiClient.get).toHaveBeenCalledWith(
        '/tracks/search?min_bpm=120&max_bpm=140',
      );
    });

    it('should handle network errors', async () => {
      const networkError = new AxiosError('Network Error');
      networkError.code = 'ECONNABORTED';
      vi.mocked(apiClient.get).mockRejectedValue(networkError);

      await expect(searchTracks({ query: 'Test' })).rejects.toThrow(
        TrackSearchError,
      );
      await expect(searchTracks({ query: 'Test' })).rejects.toThrow(
        'Erreur réseau',
      );
    });

    it('should handle server errors', async () => {
      const serverError = new AxiosError('Server Error');
      serverError.response = {
        status: 500,
        data: { error: 'Internal server error' },
      } as any;
      vi.mocked(apiClient.get).mockRejectedValue(serverError);

      await expect(searchTracks({ query: 'Test' })).rejects.toThrow(
        TrackSearchError,
      );
      await expect(searchTracks({ query: 'Test' })).rejects.toThrow(
        'Erreur serveur',
      );
    });

    it('should handle validation errors', async () => {
      const validationError = new AxiosError('Bad Request');
      validationError.response = {
        status: 400,
        data: { error: 'Invalid parameters' },
      } as any;
      vi.mocked(apiClient.get).mockRejectedValue(validationError);

      await expect(searchTracks({ query: 'Test' })).rejects.toThrow(
        TrackSearchError,
      );
      await expect(searchTracks({ query: 'Test' })).rejects.toThrow(
        'Invalid parameters',
      );
    });

    it('should handle empty params', async () => {
      const mockResponse = {
        data: {
          tracks: [],
          pagination: {
            page: 1,
            limit: 20,
            total: 0,
            total_pages: 0,
          },
        },
      };

      vi.mocked(apiClient.get).mockResolvedValue(mockResponse);

      await searchTracks({});

      expect(apiClient.get).toHaveBeenCalledWith('/tracks/search');
    });

    it('should handle pagination', async () => {
      const mockResponse = {
        data: {
          tracks: [],
          pagination: {
            page: 2,
            limit: 10,
            total: 25,
            total_pages: 3,
          },
        },
      };

      vi.mocked(apiClient.get).mockResolvedValue(mockResponse);

      const result = await searchTracks({
        page: 2,
        limit: 10,
      });

      expect(result.pagination.page).toBe(2);
      expect(result.pagination.limit).toBe(10);
      expect(result.pagination.total).toBe(25);
      expect(result.pagination.total_pages).toBe(3);
      expect(apiClient.get).toHaveBeenCalledWith(
        '/tracks/search?page=2&limit=10',
      );
    });

    it('should handle all sort options', async () => {
      const mockResponse = {
        data: {
          tracks: [],
          pagination: {
            page: 1,
            limit: 20,
            total: 0,
            total_pages: 0,
          },
        },
      };

      vi.mocked(apiClient.get).mockResolvedValue(mockResponse);

      const sortOptions: Array<TrackSearchParams['sortBy']> = [
        'popularity',
        'play_count',
        'comment_count',
        'title',
        'created_at',
        'duration',
        'artist',
      ];

      for (const sortBy of sortOptions) {
        await searchTracks({ sortBy, sortOrder: 'desc' });
        expect(apiClient.get).toHaveBeenCalledWith(
          `/tracks/search?sort_by=${sortBy}&sort_order=desc`,
        );
      }
    });
  });
});
