type BackgroundTimerMock = {
  setInterval: jest.Mock;
  clearInterval: jest.Mock;
  setTimeout: jest.Mock;
  clearTimeout: jest.Mock;
};

type SetupResult = {
  BackgroundTaskManager: typeof import('../background').BackgroundTaskManager;
  backgroundTimer: BackgroundTimerMock;
};

const createBackgroundTaskManager = (isIosValue: boolean): SetupResult => {
  const backgroundTimer: BackgroundTimerMock = {
    setInterval: jest.fn(),
    clearInterval: jest.fn(),
    setTimeout: jest.fn(),
    clearTimeout: jest.fn(),
  };

  jest.resetModules();
  jest.doMock('@react-native-firebase/messaging', () => ({}));
  jest.doMock('../socketEvents', () => ({handleNewMessage: jest.fn()}));
  jest.doMock('@shared/utils', () => ({isIos: isIosValue}));
  jest.doMock('react-native-background-timer', () => ({
    __esModule: true,
    default: backgroundTimer,
  }));

  let BackgroundTaskManager: SetupResult['BackgroundTaskManager'];

  jest.isolateModules(() => {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const module = require('../background');
    BackgroundTaskManager = module.BackgroundTaskManager;
  });

  return {BackgroundTaskManager: BackgroundTaskManager!, backgroundTimer};
};

describe('BackgroundTaskManager', () => {
  it('uses native timers on iOS', () => {
    const setIntervalSpy = jest
      .spyOn(global, 'setInterval')
      .mockReturnValue(101 as unknown as NodeJS.Timeout);
    const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
    const setTimeoutSpy = jest
      .spyOn(global, 'setTimeout')
      .mockReturnValue(202 as unknown as NodeJS.Timeout);
    const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout');

    const {BackgroundTaskManager, backgroundTimer} =
      createBackgroundTaskManager(true);

    const task = jest.fn();
    const intervalId = BackgroundTaskManager.setInterval(task, 1000);
    const timeoutId = BackgroundTaskManager.setTimeout(task, 500);

    expect(setIntervalSpy).toHaveBeenCalledWith(task, 1000);
    expect(backgroundTimer.setInterval).not.toHaveBeenCalled();
    expect(setTimeoutSpy).toHaveBeenCalledWith(task, 500);
    expect(backgroundTimer.setTimeout).not.toHaveBeenCalled();

    BackgroundTaskManager.clearInterval(intervalId);
    BackgroundTaskManager.clearTimeout(timeoutId);

    expect(clearIntervalSpy).toHaveBeenCalledWith(intervalId);
    expect(clearTimeoutSpy).toHaveBeenCalledWith(timeoutId);

    setIntervalSpy.mockRestore();
    clearIntervalSpy.mockRestore();
    setTimeoutSpy.mockRestore();
    clearTimeoutSpy.mockRestore();
  });

  it('uses background timers on android', () => {
    const {BackgroundTaskManager, backgroundTimer} =
      createBackgroundTaskManager(false);

    const task = jest.fn();
    const intervalId = 303 as unknown as ReturnType<typeof setInterval>;
    const timeoutId = 404 as unknown as ReturnType<typeof setTimeout>;

    backgroundTimer.setInterval.mockReturnValue(intervalId);
    backgroundTimer.setTimeout.mockReturnValue(timeoutId);

    expect(BackgroundTaskManager.setInterval(task, 1000)).toBe(intervalId);
    expect(backgroundTimer.setInterval).toHaveBeenCalledWith(task, 1000);

    expect(BackgroundTaskManager.setTimeout(task, 500)).toBe(timeoutId);
    expect(backgroundTimer.setTimeout).toHaveBeenCalledWith(task, 500);

    BackgroundTaskManager.clearInterval(intervalId);
    BackgroundTaskManager.clearTimeout(timeoutId);

    expect(backgroundTimer.clearInterval).toHaveBeenCalledWith(intervalId);
    expect(backgroundTimer.clearTimeout).toHaveBeenCalledWith(timeoutId);
  });
});
