import { DeviceTracking } from '../src/sensors/DeviceTracking';
import { Accelerometer, Gyroscope, DeviceMotion, Magnetometer } from 'expo-sensors';

// Create a mock implementation for expo-sensors
jest.mock('expo-sensors', () => {
  // Create a generic mock for addListener that returns an object with a remove method
  const addListener = jest.fn(() => ({
    remove: jest.fn(),
  }));
  return {
    Accelerometer: {
      setUpdateInterval: jest.fn(),
      addListener,
    },
    Gyroscope: {
      setUpdateInterval: jest.fn(),
      addListener,
    },
    DeviceMotion: {
      setUpdateInterval: jest.fn(),
      addListener,
    },
    Magnetometer: {
      setUpdateInterval: jest.fn(),
      addListener,
    },
  };
});

describe('DeviceTracking', () => {
  let tracking: DeviceTracking;

  beforeEach(() => {
    tracking = new DeviceTracking();
    jest.clearAllMocks();
  });

  it('should start and stop the accelerometer', () => {
    tracking.startAccelerometer(200);
    expect(Accelerometer.setUpdateInterval).toHaveBeenCalledWith(200);
    expect(Accelerometer.addListener).toHaveBeenCalled();

    // Capture the subscription returned by addListener
    const subscription = (Accelerometer.addListener as jest.Mock).mock.results[0].value;
    tracking.stopAccelerometer();
    // The remove() method on the subscription should have been called.
    expect(subscription.remove).toHaveBeenCalled();
  });

  it('should start and stop the gyroscope', () => {
    tracking.startGyroscope(150);
    expect(Gyroscope.setUpdateInterval).toHaveBeenCalledWith(150);
    expect(Gyroscope.addListener).toHaveBeenCalled();

    const subscription = (Gyroscope.addListener as jest.Mock).mock.results[0].value;
    tracking.stopGyroscope();
    expect(subscription.remove).toHaveBeenCalled();
  });

  it('should start and stop device motion', () => {
    tracking.startDeviceMotion(100);
    expect(DeviceMotion.setUpdateInterval).toHaveBeenCalledWith(100);
    expect(DeviceMotion.addListener).toHaveBeenCalled();

    const subscription = (DeviceMotion.addListener as jest.Mock).mock.results[0].value;
    tracking.stopDeviceMotion();
    expect(subscription.remove).toHaveBeenCalled();
  });

  it('should start and stop the magnetometer', () => {
    tracking.startMagnetometer(120);
    expect(Magnetometer.setUpdateInterval).toHaveBeenCalledWith(120);
    expect(Magnetometer.addListener).toHaveBeenCalled();

    const subscription = (Magnetometer.addListener as jest.Mock).mock.results[0].value;
    tracking.stopMagnetometer();
    expect(subscription.remove).toHaveBeenCalled();
  });

  it('should stop all sensors', () => {
    tracking.startAccelerometer();
    tracking.startGyroscope();
    tracking.startDeviceMotion();
    tracking.startMagnetometer();
    tracking.stopAllSensors();
    // Expect that for each sensor the subscription's remove was called.
    expect((Accelerometer.addListener as jest.Mock).mock.results[0].value.remove).toHaveBeenCalled();
    expect((Gyroscope.addListener as jest.Mock).mock.results[0].value.remove).toHaveBeenCalled();
    expect((DeviceMotion.addListener as jest.Mock).mock.results[0].value.remove).toHaveBeenCalled();
    expect((Magnetometer.addListener as jest.Mock).mock.results[0].value.remove).toHaveBeenCalled();
  });
});