import { describe, expect, it, vi } from 'vitest';
import { Result } from '@core/shared/application/Result';
import { MediaService } from './MediaService';

describe('MediaService', () => {
  const logger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() };

  it('requestAvatarGeneration returns presenter response on success', async () => {
    const requestAvatarGenerationPresenter = {
      responseModel: { success: true, requestId: 'r1', avatarUrls: ['u1'], errorMessage: '' },
    };

    const requestAvatarGenerationUseCase = {
      execute: vi.fn(async () => Result.ok(undefined)),
    };

    const service = new MediaService(
      requestAvatarGenerationUseCase as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      logger as any,
      requestAvatarGenerationPresenter as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(
      service.requestAvatarGeneration({ userId: 'u1', facePhotoData: {} as any, suitColor: 'red' as any }),
    ).resolves.toEqual({ success: true, requestId: 'r1', avatarUrls: ['u1'], errorMessage: '' });

    expect(requestAvatarGenerationUseCase.execute).toHaveBeenCalledWith({
      userId: 'u1',
      facePhotoData: {} as any,
      suitColor: 'red',
    });
  });

  it('requestAvatarGeneration returns failure DTO on error', async () => {
    const service = new MediaService(
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'fail' } })) } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: { success: true } } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(
      service.requestAvatarGeneration({ userId: 'u1', facePhotoData: {} as any, suitColor: 'red' as any }),
    ).resolves.toEqual({
      success: false,
      requestId: '',
      avatarUrls: [],
      errorMessage: 'fail',
    });
  });

  it('uploadMedia returns presenter response on success', async () => {
    const uploadMediaPresenter = { responseModel: { success: true, mediaId: 'm1' } };
    const uploadMediaUseCase = { execute: vi.fn(async () => Result.ok(undefined)) };

    const service = new MediaService(
      { execute: vi.fn() } as any,
      uploadMediaUseCase as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: {} } as any,
      uploadMediaPresenter as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(
      service.uploadMedia({ file: {} as any, userId: 'u1', metadata: { a: 1 } } as any),
    ).resolves.toEqual({
      success: true,
      mediaId: 'm1',
    });

    expect(uploadMediaUseCase.execute).toHaveBeenCalledWith({
      file: {} as any,
      uploadedBy: 'u1',
      metadata: { a: 1 },
    });
  });

  it('uploadMedia uses empty uploadedBy when userId missing', async () => {
    const uploadMediaUseCase = { execute: vi.fn(async () => Result.ok(undefined)) };

    const service = new MediaService(
      { execute: vi.fn() } as any,
      uploadMediaUseCase as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: { success: true, mediaId: 'm1' } } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(service.uploadMedia({ file: {} as any } as any)).resolves.toEqual({ success: true, mediaId: 'm1' });
    expect(uploadMediaUseCase.execute).toHaveBeenCalledWith({ file: {} as any, uploadedBy: '', metadata: {} });
  });

  it('uploadMedia returns failure DTO on error', async () => {
    const uploadMediaUseCase = {
      execute: vi.fn(async () => Result.err({ code: 'UPLOAD_FAILED', details: { message: 'nope' } })),
    };

    const service = new MediaService(
      { execute: vi.fn() } as any,
      uploadMediaUseCase as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: { success: true } } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(service.uploadMedia({ file: {} as any, userId: 'u1' } as any)).resolves.toEqual({
      success: false,
      error: 'nope',
    });
  });

  it('getMedia returns presenter response on success', async () => {
    const getMediaUseCase = { execute: vi.fn(async () => Result.ok(undefined)) };
    const getMediaPresenter = { responseModel: { mediaId: 'm1' } };

    const service = new MediaService(
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      getMediaUseCase as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      getMediaPresenter as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(service.getMedia('m1')).resolves.toEqual({ mediaId: 'm1' });
    expect(getMediaUseCase.execute).toHaveBeenCalledWith({ mediaId: 'm1' });
  });

  it('getMedia returns null on MEDIA_NOT_FOUND', async () => {
    const service = new MediaService(
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn(async () => Result.err({ code: 'MEDIA_NOT_FOUND', details: { message: 'n/a' } })) } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(service.getMedia('m1')).resolves.toBeNull();
  });

  it('getMedia throws on non-not-found error', async () => {
    const service = new MediaService(
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'boom' } })) } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(service.getMedia('m1')).rejects.toThrow('boom');
  });

  it('deleteMedia returns presenter response on success', async () => {
    const deleteMediaUseCase = { execute: vi.fn(async () => Result.ok(undefined)) };
    const deleteMediaPresenter = { responseModel: { success: true } };

    const service = new MediaService(
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      deleteMediaUseCase as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      deleteMediaPresenter as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(service.deleteMedia('m1')).resolves.toEqual({ success: true });
    expect(deleteMediaUseCase.execute).toHaveBeenCalledWith({ mediaId: 'm1' });
  });

  it('deleteMedia returns failure DTO on error', async () => {
    const service = new MediaService(
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'nope' } })) } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: { success: true } } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(service.deleteMedia('m1')).resolves.toEqual({ success: false, error: 'nope' });
  });

  it('getAvatar returns presenter response on success', async () => {
    const getAvatarUseCase = { execute: vi.fn(async () => Result.ok(undefined)) };
    const getAvatarPresenter = { responseModel: { avatarUrl: 'u1' } };

    const service = new MediaService(
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      getAvatarUseCase as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      getAvatarPresenter as any,
      { responseModel: {} } as any,
    );

    await expect(service.getAvatar('d1')).resolves.toEqual({ avatarUrl: 'u1' });
    expect(getAvatarUseCase.execute).toHaveBeenCalledWith({ driverId: 'd1' });
  });

  it('getAvatar returns null on AVATAR_NOT_FOUND', async () => {
    const service = new MediaService(
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn(async () => Result.err({ code: 'AVATAR_NOT_FOUND', details: { message: 'n/a' } })) } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(service.getAvatar('d1')).resolves.toBeNull();
  });

  it('getAvatar throws on non-not-found error', async () => {
    const service = new MediaService(
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'boom' } })) } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(service.getAvatar('d1')).rejects.toThrow('boom');
  });

  it('updateAvatar returns presenter response on success', async () => {
    const updateAvatarUseCase = { execute: vi.fn(async () => Result.ok(undefined)) };
    const updateAvatarPresenter = { responseModel: { success: true } };

    const service = new MediaService(
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      updateAvatarUseCase as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      updateAvatarPresenter as any,
    );

    await expect(service.updateAvatar('d1', { avatarUrl: 'u1' } as any)).resolves.toEqual({ success: true });
    expect(updateAvatarUseCase.execute).toHaveBeenCalledWith({ driverId: 'd1', mediaUrl: 'u1' });
  });

  it('updateAvatar returns failure DTO on error', async () => {
    const service = new MediaService(
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'nope' } })) } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: { success: true } } as any,
    );

    await expect(service.updateAvatar('d1', { avatarUrl: 'u' } as any)).resolves.toEqual({
      success: false,
      error: 'nope',
    });
  });

  it('requestAvatarGeneration uses fallback errorMessage when no details.message', async () => {
    const service = new MediaService(
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR' } as any)) } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: { success: true } } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(
      service.requestAvatarGeneration({ userId: 'u1', facePhotoData: {} as any, suitColor: 'red' as any }),
    ).resolves.toEqual({
      success: false,
      requestId: '',
      avatarUrls: [],
      errorMessage: 'Failed to request avatar generation',
    });
  });

  it('uploadMedia uses fallback error when no details.message', async () => {
    const uploadMediaUseCase = {
      execute: vi.fn(async () => Result.err({ code: 'UPLOAD_FAILED' } as any)),
    };

    const service = new MediaService(
      { execute: vi.fn() } as any,
      uploadMediaUseCase as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: { success: true } } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(service.uploadMedia({ file: {} as any, userId: 'u1' } as any)).resolves.toEqual({
      success: false,
      error: 'Upload failed',
    });
  });

  it('getMedia throws fallback message when no details.message and not not-found', async () => {
    const service = new MediaService(
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR' } as any)) } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(service.getMedia('m1')).rejects.toThrow('Failed to get media');
  });

  it('deleteMedia uses fallback message when no details.message', async () => {
    const service = new MediaService(
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR' } as any)) } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: { success: true } } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(service.deleteMedia('m1')).resolves.toEqual({ success: false, error: 'Failed to delete media' });
  });

  it('getAvatar throws fallback message when no details.message and not not-found', async () => {
    const service = new MediaService(
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR' } as any)) } as any,
      { execute: vi.fn() } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
    );

    await expect(service.getAvatar('d1')).rejects.toThrow('Failed to get avatar');
  });

  it('updateAvatar uses fallback message when no details.message', async () => {
    const service = new MediaService(
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn() } as any,
      { execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR' } as any)) } as any,
      logger as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: {} } as any,
      { responseModel: { success: true } } as any,
    );

    await expect(service.updateAvatar('d1', { avatarUrl: 'u' } as any)).resolves.toEqual({
      success: false,
      error: 'Failed to update avatar',
    });
  });
});
