import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
  createShare,
  getSharedTrack,
  revokeShare,
  TrackShareError,
} from './trackShareService';
import { apiClient } from '@/services/api/client';
import { AxiosError } from 'axios';
import type { Track } from '../types/track';
import type { TrackShare } from './trackShareService';

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

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

  describe('createShare', () => {
    it('should create a share successfully', async () => {
      const mockShare: TrackShare = {
        id: 1,
        track_id: 123,
        creator_id: 456,
        share_token: 'test-token-123',
        permissions: 'read,download',
        access_count: 0,
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-01T00:00:00Z',
      };

      const mockResponse = {
        data: {
          share: mockShare,
        },
      };

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

      const result = await createShare(123, {
        permissions: 'read,download',
      });

      expect(result).toEqual(mockShare);
      expect(apiClient.post).toHaveBeenCalledWith('/tracks/123/share', {
        permissions: 'read,download',
      });
    });

    it('should create a share with expiration', async () => {
      const mockShare: TrackShare = {
        id: 1,
        track_id: 123,
        creator_id: 456,
        share_token: 'test-token-123',
        permissions: 'read',
        expires_at: '2024-12-31T23:59:59Z',
        access_count: 0,
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-01T00:00:00Z',
      };

      const mockResponse = {
        data: {
          share: mockShare,
        },
      };

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

      const result = await createShare(123, {
        permissions: 'read',
        expires_at: '2024-12-31T23:59:59Z',
      });

      expect(result).toEqual(mockShare);
      expect(apiClient.post).toHaveBeenCalledWith('/tracks/123/share', {
        permissions: 'read',
        expires_at: '2024-12-31T23:59:59Z',
      });
    });

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

      await expect(createShare(123, { permissions: 'read' })).rejects.toThrow(
        TrackShareError,
      );
      await expect(createShare(123, { permissions: 'read' })).rejects.toThrow(
        'Accès refusé',
      );
    });

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

      await expect(createShare(123, { permissions: 'read' })).rejects.toThrow(
        TrackShareError,
      );
      await expect(createShare(123, { permissions: 'read' })).rejects.toThrow(
        'Track introuvable',
      );
    });

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

      await expect(createShare(123, { permissions: 'read' })).rejects.toThrow(
        TrackShareError,
      );
      await expect(createShare(123, { permissions: 'read' })).rejects.toThrow(
        'Erreur réseau',
      );
    });
  });

  describe('getSharedTrack', () => {
    it('should get shared track successfully', async () => {
      const mockTrack: Track = {
        id: 123,
        creator_id: 456,
        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 mockShare: TrackShare = {
        id: 1,
        track_id: 123,
        creator_id: 456,
        share_token: 'test-token-123',
        permissions: 'read,download',
        access_count: 1,
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-01T00:00:00Z',
      };

      const mockResponse = {
        data: {
          track: mockTrack,
          share: mockShare,
        },
      };

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

      const result = await getSharedTrack('test-token-123');

      expect(result.track).toEqual(mockTrack);
      expect(result.share).toEqual(mockShare);
      expect(apiClient.get).toHaveBeenCalledWith(
        '/tracks/shared/test-token-123',
      );
    });

    it('should handle invalid token error', async () => {
      const error = new AxiosError('Not found');
      error.response = {
        status: 404,
        data: { error: 'invalid share token' },
      } as any;
      vi.mocked(apiClient.get).mockRejectedValue(error);

      await expect(getSharedTrack('invalid-token')).rejects.toThrow(
        TrackShareError,
      );
      await expect(getSharedTrack('invalid-token')).rejects.toThrow(
        'Lien de partage invalide',
      );
    });

    it('should handle expired token error', async () => {
      const error = new AxiosError('Forbidden');
      error.response = {
        status: 403,
        data: { error: 'share link expired' },
      } as any;
      vi.mocked(apiClient.get).mockRejectedValue(error);

      await expect(getSharedTrack('expired-token')).rejects.toThrow(
        TrackShareError,
      );
      await expect(getSharedTrack('expired-token')).rejects.toThrow(
        'Lien de partage expiré',
      );
    });

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

      await expect(getSharedTrack('test-token')).rejects.toThrow(
        TrackShareError,
      );
      await expect(getSharedTrack('test-token')).rejects.toThrow(
        'Erreur réseau',
      );
    });
  });

  describe('revokeShare', () => {
    it('should revoke share successfully', async () => {
      vi.mocked(apiClient.delete).mockResolvedValue({ data: {} });

      await revokeShare(1);

      expect(apiClient.delete).toHaveBeenCalledWith('/tracks/shares/1');
    });

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

      await expect(revokeShare(1)).rejects.toThrow(TrackShareError);
      await expect(revokeShare(1)).rejects.toThrow('Accès refusé');
    });

    it('should handle not found error', async () => {
      const error = new AxiosError('Not found');
      error.response = {
        status: 404,
        data: { error: 'share not found' },
      } as any;
      vi.mocked(apiClient.delete).mockRejectedValue(error);

      await expect(revokeShare(999)).rejects.toThrow(TrackShareError);
      await expect(revokeShare(999)).rejects.toThrow(
        'Lien de partage introuvable',
      );
    });

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

      await expect(revokeShare(1)).rejects.toThrow(TrackShareError);
      await expect(revokeShare(1)).rejects.toThrow('Erreur réseau');
    });
  });
});
