import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { NotFoundException, BadRequestException } from '@nestjs/common';
import { DeviceTrustService } from './device-trust.service';
import { DeviceRegistry } from './entities/device-registry.entity';

describe('DeviceTrustService', () => {
  let service: DeviceTrustService;
  let deviceRepository: Repository<DeviceRegistry>;

  const mockDevice = {
    id: 'device_123',
    userId: 'user_123',
    deviceId: 'fingerprint_abc',
    isTrusted: false,
    deviceInfo: {
      platform: 'ios',
      model: 'iPhone 14',
      osVersion: '17.0',
    },
    lastSeenAt: new Date(),
    createdAt: new Date(),
  };

  const mockDeviceRepository = {
    find: jest.fn(),
    findOne: jest.fn(),
    count: jest.fn(),
    update: jest.fn(),
    delete: jest.fn(),
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        DeviceTrustService,
        {
          provide: getRepositoryToken(DeviceRegistry),
          useValue: mockDeviceRepository,
        },
      ],
    }).compile();

    service = module.get<DeviceTrustService>(DeviceTrustService);
    deviceRepository = module.get<Repository<DeviceRegistry>>(getRepositoryToken(DeviceRegistry));
    jest.clearAllMocks();
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  describe('getUserDevices', () => {
    it('should return all devices for a user', async () => {
      mockDeviceRepository.find.mockResolvedValue([mockDevice]);
      const result = await service.getUserDevices('user_123');
      expect(result).toHaveLength(1);
      expect(result[0]).toEqual(expect.objectContaining({
        id: 'device_123',
        deviceId: 'fingerprint_abc',
        isTrusted: false,
      }));
    });
  });

  describe('getTrustedDevices', () => {
    it('should return only trusted devices', async () => {
      const trustedDevice = { ...mockDevice, isTrusted: true };
      mockDeviceRepository.find.mockResolvedValue([trustedDevice]);
      const result = await service.getTrustedDevices('user_123');
      expect(result).toHaveLength(1);
      expect(result[0].isTrusted).toBe(true);
    });
  });

  describe('trustDevice', () => {
    it('should mark device as trusted', async () => {
      mockDeviceRepository.findOne.mockResolvedValue(mockDevice);
      mockDeviceRepository.update.mockResolvedValue({ affected: 1 });
      const result = await service.trustDevice('user_123', 'device_123');
      expect(result.success).toBe(true);
      expect(deviceRepository.update).toHaveBeenCalledWith('device_123', { isTrusted: true });
    });

    it('should throw NotFoundException if device not found', async () => {
      mockDeviceRepository.findOne.mockResolvedValue(null);
      await expect(service.trustDevice('user_123', 'device_123')).rejects.toThrow(NotFoundException);
    });
  });

  describe('revokeDeviceTrust', () => {
    it('should revoke device trust', async () => {
      mockDeviceRepository.findOne.mockResolvedValue({ ...mockDevice, isTrusted: true });
      mockDeviceRepository.update.mockResolvedValue({ affected: 1 });
      const result = await service.revokeDeviceTrust('user_123', 'device_123');
      expect(result.success).toBe(true);
      expect(deviceRepository.update).toHaveBeenCalledWith('device_123', { isTrusted: false });
    });
  });

  describe('removeDevice', () => {
    it('should remove a device', async () => {
      mockDeviceRepository.findOne.mockResolvedValue(mockDevice);
      mockDeviceRepository.delete.mockResolvedValue({ affected: 1 });
      const result = await service.removeDevice('user_123', 'device_123');
      expect(result.success).toBe(true);
      expect(deviceRepository.delete).toHaveBeenCalledWith('device_123');
    });
  });

  describe('getDeviceCount', () => {
    it('should return device counts', async () => {
      mockDeviceRepository.count.mockResolvedValueOnce(5).mockResolvedValueOnce(3);
      const result = await service.getDeviceCount('user_123');
      expect(result.total).toBe(5);
      expect(result.trusted).toBe(3);
    });
  });

  describe('isDeviceTrusted', () => {
    it('should return true for trusted device', async () => {
      mockDeviceRepository.findOne.mockResolvedValue({ ...mockDevice, isTrusted: true });
      const result = await service.isDeviceTrusted('user_123', 'device_123');
      expect(result).toBe(true);
    });

    it('should return false for untrusted device', async () => {
      mockDeviceRepository.findOne.mockResolvedValue(mockDevice);
      const result = await service.isDeviceTrusted('user_123', 'device_123');
      expect(result).toBe(false);
    });
  });
});
