import { BadRequestException } from '@nestjs/common';
import { DevicesService } from './devices.service';

describe('DevicesService', () => {
  const createService = () => {
    const devicesRepository = {
      upsert: jest.fn(),
      deactivate: jest.fn(),
    };

    return {
      service: new DevicesService(devicesRepository as any),
      devicesRepository,
    };
  };

  it('trims registration data and returns the persisted device', async () => {
    const { service, devicesRepository } = createService();
    const device = { id: 'device-1', isActive: true };
    devicesRepository.upsert.mockResolvedValue(device);

    await expect(
      service.register('user-1', {
        fcmToken: '  token  ',
        platform: 'android',
        deviceId: ' device-id ',
        appVersion: ' 1.2.3 ',
        locale: ' ar ',
      }),
    ).resolves.toEqual({ message: 'Device registered successfully', device });
    expect(devicesRepository.upsert).toHaveBeenCalledWith('user-1', {
      fcmToken: 'token',
      platform: 'android',
      deviceId: 'device-id',
      appVersion: '1.2.3',
      locale: 'ar',
    });
  });

  it('normalizes absent optional registration data', async () => {
    const { service, devicesRepository } = createService();
    devicesRepository.upsert.mockResolvedValue(null);

    await service.register('user-1', { fcmToken: 'token', platform: 'ios' });

    expect(devicesRepository.upsert).toHaveBeenCalledWith('user-1', {
      fcmToken: 'token',
      platform: 'ios',
      deviceId: '',
      appVersion: '',
      locale: '',
    });
  });

  it('rejects an empty FCM token without touching the repository', async () => {
    const { service, devicesRepository } = createService();

    await expect(
      service.register('user-1', { fcmToken: '   ', platform: 'web' }),
    ).rejects.toBeInstanceOf(BadRequestException);
    expect(devicesRepository.upsert).not.toHaveBeenCalled();
  });

  it('unregisters by trimmed identifiers', async () => {
    const { service, devicesRepository } = createService();
    const device = { id: 'device-1', isActive: false };
    devicesRepository.deactivate.mockResolvedValue(device);

    await expect(
      service.unregister('user-1', { deviceId: ' device-id ', fcmToken: ' token ' }),
    ).resolves.toEqual({ message: 'Device unregistered successfully', device });
    expect(devicesRepository.deactivate).toHaveBeenCalledWith('user-1', {
      deviceId: 'device-id',
      fcmToken: 'token',
    });
  });

  it('supports unregistering by token alone', async () => {
    const { service, devicesRepository } = createService();
    devicesRepository.deactivate.mockResolvedValue(null);

    await service.unregister('user-1', { fcmToken: ' token ' });

    expect(devicesRepository.deactivate).toHaveBeenCalledWith('user-1', {
      deviceId: undefined,
      fcmToken: 'token',
    });
  });

  it('rejects unregister without a usable identifier', async () => {
    const { service, devicesRepository } = createService();

    await expect(
      service.unregister('user-1', { deviceId: ' ', fcmToken: '  ' }),
    ).rejects.toThrow('deviceId or fcmToken is required');
    expect(devicesRepository.deactivate).not.toHaveBeenCalled();
  });

  it('propagates repository failures', async () => {
    const { service, devicesRepository } = createService();
    devicesRepository.upsert.mockRejectedValue(new Error('database unavailable'));
    devicesRepository.deactivate.mockRejectedValue(new Error('database unavailable'));

    await expect(
      service.register('user-1', { fcmToken: 'token', platform: 'android' }),
    ).rejects.toThrow('database unavailable');
    await expect(service.unregister('user-1', { deviceId: 'id' })).rejects.toThrow(
      'database unavailable',
    );
  });
});
