import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
  createVersion,
  listVersions,
  getVersion,
  restoreVersion,
  TrackVersionError,
  TrackVersion,
  CreateVersionRequest,
} from './trackVersionService';
import { apiClient } from '@/services/api/client';
import { AxiosError } from 'axios';

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

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

  describe('createVersion', () => {
    it('should create a version successfully', async () => {
      const mockVersion: TrackVersion = {
        id: 1,
        track_id: 123,
        version_number: 1,
        file_path: '/path/to/version.mp3',
        file_size: 1024,
        changelog: 'Initial version',
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-01T00:00:00Z',
      };

      vi.mocked(apiClient.post).mockResolvedValue({
        data: { version: mockVersion },
      } as any);

      const request: CreateVersionRequest = {
        file_path: '/path/to/version.mp3',
        file_size: 1024,
        changelog: 'Initial version',
      };

      const result = await createVersion(123, request);

      expect(result).toEqual(mockVersion);
      expect(apiClient.post).toHaveBeenCalledWith(
        '/tracks/123/versions',
        request,
      );
    });

    it('should handle 404 error (track not found)', async () => {
      const error = new AxiosError('Not Found');
      error.response = {
        status: 404,
        data: { error: 'track not found' },
      } as any;

      vi.mocked(apiClient.post).mockRejectedValue(error);

      const request: CreateVersionRequest = {
        file_path: '/path/to/version.mp3',
        file_size: 1024,
      };

      await expect(createVersion(999, request)).rejects.toThrow(
        TrackVersionError,
      );
      await expect(createVersion(999, request)).rejects.toThrow(
        'Track introuvable',
      );
    });

    it('should handle 403 error (forbidden)', async () => {
      const error = new AxiosError('Forbidden');
      error.response = {
        status: 403,
        data: { error: 'forbidden' },
      } as any;

      vi.mocked(apiClient.post).mockRejectedValue(error);

      const request: CreateVersionRequest = {
        file_path: '/path/to/version.mp3',
        file_size: 1024,
      };

      await expect(createVersion(123, request)).rejects.toThrow(
        TrackVersionError,
      );
      await expect(createVersion(123, request)).rejects.toThrow('Accès refusé');
    });

    it('should handle network errors', async () => {
      const error = new AxiosError('Network Error');
      error.code = 'ECONNABORTED';

      vi.mocked(apiClient.post).mockRejectedValue(error);

      const request: CreateVersionRequest = {
        file_path: '/path/to/version.mp3',
        file_size: 1024,
      };

      await expect(createVersion(123, request)).rejects.toThrow(
        TrackVersionError,
      );
      await expect(createVersion(123, request)).rejects.toThrow(
        'Erreur réseau',
      );
    });
  });

  describe('listVersions', () => {
    it('should list versions successfully', async () => {
      const mockVersions: TrackVersion[] = [
        {
          id: 1,
          track_id: 123,
          version_number: 1,
          file_path: '/path/to/v1.mp3',
          file_size: 1024,
          changelog: 'Version 1',
          created_at: '2024-01-01T00:00:00Z',
          updated_at: '2024-01-01T00:00:00Z',
        },
        {
          id: 2,
          track_id: 123,
          version_number: 2,
          file_path: '/path/to/v2.mp3',
          file_size: 2048,
          changelog: 'Version 2',
          created_at: '2024-01-02T00:00:00Z',
          updated_at: '2024-01-02T00:00:00Z',
        },
      ];

      vi.mocked(apiClient.get).mockResolvedValue({
        data: { versions: mockVersions },
      } as any);

      const result = await listVersions(123);

      expect(result).toEqual(mockVersions);
      expect(apiClient.get).toHaveBeenCalledWith('/tracks/123/versions');
    });

    it('should handle 404 error', async () => {
      const error = new AxiosError('Not Found');
      error.response = {
        status: 404,
        data: { error: 'track not found' },
      } as any;

      vi.mocked(apiClient.get).mockRejectedValue(error);

      await expect(listVersions(999)).rejects.toThrow(TrackVersionError);
      await expect(listVersions(999)).rejects.toThrow('Track introuvable');
    });

    it('should handle empty versions list', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({
        data: { versions: [] },
      } as any);

      const result = await listVersions(123);

      expect(result).toEqual([]);
      expect(result.length).toBe(0);
    });
  });

  describe('getVersion', () => {
    it('should get version by ID successfully', async () => {
      const mockVersion: TrackVersion = {
        id: 1,
        track_id: 123,
        version_number: 1,
        file_path: '/path/to/v1.mp3',
        file_size: 1024,
        changelog: 'Version 1',
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-01T00:00:00Z',
      };

      vi.mocked(apiClient.get).mockResolvedValue({
        data: { version: mockVersion },
      } as any);

      const result = await getVersion(123, 1);

      expect(result).toEqual(mockVersion);
      expect(apiClient.get).toHaveBeenCalledWith('/tracks/123/versions/1');
    });

    it('should get version by number successfully', async () => {
      const mockVersion: TrackVersion = {
        id: 1,
        track_id: 123,
        version_number: 2,
        file_path: '/path/to/v2.mp3',
        file_size: 2048,
        changelog: 'Version 2',
        created_at: '2024-01-02T00:00:00Z',
        updated_at: '2024-01-02T00:00:00Z',
      };

      vi.mocked(apiClient.get).mockResolvedValue({
        data: { version: mockVersion },
      } as any);

      const result = await getVersion(123, '2');

      expect(result).toEqual(mockVersion);
      expect(apiClient.get).toHaveBeenCalledWith('/tracks/123/versions/2');
    });

    it('should handle 404 error', async () => {
      const error = new AxiosError('Not Found');
      error.response = {
        status: 404,
        data: { error: 'version not found' },
      } as any;

      vi.mocked(apiClient.get).mockRejectedValue(error);

      await expect(getVersion(123, 999)).rejects.toThrow(TrackVersionError);
      await expect(getVersion(123, 999)).rejects.toThrow('Version introuvable');
    });
  });

  describe('restoreVersion', () => {
    it('should restore version successfully', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({
        data: { message: 'version restored successfully' },
      } as any);

      await restoreVersion(123, 1);

      expect(apiClient.post).toHaveBeenCalledWith(
        '/tracks/123/versions/1/restore',
      );
    });

    it('should handle 404 error', async () => {
      const error = new AxiosError('Not Found');
      error.response = {
        status: 404,
        data: { error: 'version not found' },
      } as any;

      vi.mocked(apiClient.post).mockRejectedValue(error);

      await expect(restoreVersion(123, 999)).rejects.toThrow(TrackVersionError);
      await expect(restoreVersion(123, 999)).rejects.toThrow(
        'Version ou track introuvable',
      );
    });

    it('should handle 403 error (forbidden)', async () => {
      const error = new AxiosError('Forbidden');
      error.response = {
        status: 403,
        data: { error: 'forbidden' },
      } as any;

      vi.mocked(apiClient.post).mockRejectedValue(error);

      await expect(restoreVersion(123, 1)).rejects.toThrow(TrackVersionError);
      await expect(restoreVersion(123, 1)).rejects.toThrow('Accès refusé');
    });

    it('should handle 401 error (unauthorized)', async () => {
      const error = new AxiosError('Unauthorized');
      error.response = {
        status: 401,
        data: { error: 'unauthorized' },
      } as any;

      vi.mocked(apiClient.post).mockRejectedValue(error);

      await expect(restoreVersion(123, 1)).rejects.toThrow(TrackVersionError);
      await expect(restoreVersion(123, 1)).rejects.toThrow('Non autorisé');
    });

    it('should handle network errors', async () => {
      const error = new AxiosError('Network Error');
      error.code = 'ETIMEDOUT';

      vi.mocked(apiClient.post).mockRejectedValue(error);

      await expect(restoreVersion(123, 1)).rejects.toThrow(TrackVersionError);
      await expect(restoreVersion(123, 1)).rejects.toThrow('Erreur réseau');
    });

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

      vi.mocked(apiClient.post).mockRejectedValue(error);

      await expect(restoreVersion(123, 1)).rejects.toThrow(TrackVersionError);
      await expect(restoreVersion(123, 1)).rejects.toThrow('Erreur serveur');
    });
  });
});
