import { describe, it, expect, vi, Mocked } from 'vitest';
import { MediaService } from './MediaService';
import { MediaApiClient } from '../../api/media/MediaApiClient';
import { MediaViewModel } from '../../view-models/MediaViewModel';
import { UploadMediaViewModel } from '../../view-models/UploadMediaViewModel';
import { DeleteMediaViewModel } from '../../view-models/DeleteMediaViewModel';

describe('MediaService', () => {
  let mockApiClient: Mocked<MediaApiClient>;
  let service: MediaService;

  beforeEach(() => {
    mockApiClient = {
      uploadMedia: vi.fn(),
      getMedia: vi.fn(),
      deleteMedia: vi.fn(),
      requestAvatarGeneration: vi.fn(),
      getAvatar: vi.fn(),
      updateAvatar: vi.fn(),
      validateFacePhoto: vi.fn(),
    } as unknown as Mocked<MediaApiClient>;

    service = new MediaService(mockApiClient);
  });

  describe('uploadMedia', () => {
    it('should call apiClient.uploadMedia with correct input and return view model', async () => {
      const input = {
        file: new File(['test'], 'test.jpg', { type: 'image/jpeg' }),
        type: 'image',
        category: 'avatar' as const,
      };

      const expectedOutput = { success: true, mediaId: 'media-123', url: 'https://example.com/media.jpg' };
      mockApiClient.uploadMedia.mockResolvedValue(expectedOutput);

      const result = await service.uploadMedia(input);

      expect(mockApiClient.uploadMedia).toHaveBeenCalledWith(input);
      expect(result).toBeInstanceOf(UploadMediaViewModel);
      expect(result.success).toBe(true);
      expect(result.mediaId).toBe('media-123');
      expect(result.url).toBe('https://example.com/media.jpg');
      expect(result.isSuccessful).toBe(true);
      expect(result.hasError).toBe(false);
    });

    it('should handle error response', async () => {
      const input = {
        file: new File(['test'], 'test.jpg', { type: 'image/jpeg' }),
        type: 'image',
      };

      const expectedOutput = { success: false, error: 'Upload failed' };
      mockApiClient.uploadMedia.mockResolvedValue(expectedOutput);

      const result = await service.uploadMedia(input);

      expect(result).toBeInstanceOf(UploadMediaViewModel);
      expect(result.success).toBe(false);
      expect(result.error).toBe('Upload failed');
      expect(result.isSuccessful).toBe(false);
      expect(result.hasError).toBe(true);
    });

    it('should handle upload without category', async () => {
      const input = {
        file: new File(['test'], 'test.jpg', { type: 'image/jpeg' }),
        type: 'image',
      };

      const expectedOutput = { success: true, mediaId: 'media-456', url: 'https://example.com/media2.jpg' };
      mockApiClient.uploadMedia.mockResolvedValue(expectedOutput);

      const result = await service.uploadMedia(input);

      expect(mockApiClient.uploadMedia).toHaveBeenCalledWith(input);
      expect(result).toBeInstanceOf(UploadMediaViewModel);
      expect(result.success).toBe(true);
    });
  });

  describe('getMedia', () => {
    it('should call apiClient.getMedia with mediaId and return view model', async () => {
      const mediaId = 'media-123';

      const expectedOutput = {
        id: 'media-123',
        url: 'https://example.com/image.jpg',
        type: 'image',
        category: 'avatar',
        uploadedAt: '2023-01-15T00:00:00.000Z',
        size: 2048000,
      };
      mockApiClient.getMedia.mockResolvedValue(expectedOutput);

      const result = await service.getMedia(mediaId);

      expect(mockApiClient.getMedia).toHaveBeenCalledWith(mediaId);
      expect(result).toBeInstanceOf(MediaViewModel);
      expect(result.id).toBe('media-123');
      expect(result.url).toBe('https://example.com/image.jpg');
      expect(result.type).toBe('image');
      expect(result.category).toBe('avatar');
      expect(result.uploadedAt).toEqual(new Date('2023-01-15T00:00:00.000Z'));
      expect(result.size).toBe(2048000);
      expect(result.formattedSize).toBe('1.95 MB');
    });

    it('should handle media without category and size', async () => {
      const mediaId = 'media-456';

      const expectedOutput = {
        id: 'media-456',
        url: 'https://example.com/video.mp4',
        type: 'video',
        uploadedAt: '2023-02-20T00:00:00.000Z',
      };
      mockApiClient.getMedia.mockResolvedValue(expectedOutput);

      const result = await service.getMedia(mediaId);

      expect(result).toBeInstanceOf(MediaViewModel);
      expect(result.id).toBe('media-456');
      expect(result.type).toBe('video');
      expect(result.category).toBeUndefined();
      expect(result.size).toBeUndefined();
      expect(result.formattedSize).toBe('Unknown');
    });
  });

  describe('deleteMedia', () => {
    it('should call apiClient.deleteMedia with mediaId and return view model', async () => {
      const mediaId = 'media-123';

      const expectedOutput = { success: true };
      mockApiClient.deleteMedia.mockResolvedValue(expectedOutput);

      const result = await service.deleteMedia(mediaId);

      expect(mockApiClient.deleteMedia).toHaveBeenCalledWith(mediaId);
      expect(result).toBeInstanceOf(DeleteMediaViewModel);
      expect(result.success).toBe(true);
      expect(result.isSuccessful).toBe(true);
      expect(result.hasError).toBe(false);
    });

    it('should handle error response', async () => {
      const mediaId = 'media-456';

      const expectedOutput = { success: false, error: 'Deletion failed' };
      mockApiClient.deleteMedia.mockResolvedValue(expectedOutput);

      const result = await service.deleteMedia(mediaId);

      expect(result).toBeInstanceOf(DeleteMediaViewModel);
      expect(result.success).toBe(false);
      expect(result.error).toBe('Deletion failed');
      expect(result.isSuccessful).toBe(false);
      expect(result.hasError).toBe(true);
    });
  });
});