import { OnlineDetectionService } from '../../../../src/domain/services/online-detection.service';
import { TimeWindow } from '../../../../src/domain/value-objects/time-window.vo';

describe('OnlineDetectionService', () => {
  let service: OnlineDetectionService;

  beforeEach(() => {
    service = new OnlineDetectionService();
  });

  describe('isOnline', () => {
    it('should return true for recent heartbeat', () => {
      const now = new Date();
      const recentTimestamp = Math.floor(now.getTime() / 1000) - 60; // 60 seconds ago

      expect(service.isOnline(recentTimestamp, now)).toBe(true);
    });

    it('should return true for heartbeat within window', () => {
      const now = new Date();
      const withinWindow = Math.floor(now.getTime() / 1000) - 179; // 179 seconds ago (within 180 window)

      expect(service.isOnline(withinWindow, now)).toBe(true);
    });

    it('should return false for heartbeat at window boundary', () => {
      const now = new Date();
      const atBoundary = Math.floor(now.getTime() / 1000) - 180; // Exactly 180 seconds ago

      expect(service.isOnline(atBoundary, now)).toBe(false);
    });

    it('should return false for old heartbeat', () => {
      const now = new Date();
      const oldTimestamp = Math.floor(now.getTime() / 1000) - 300; // 5 minutes ago

      expect(service.isOnline(oldTimestamp, now)).toBe(false);
    });

    it('should return false for very old heartbeat', () => {
      const now = new Date();
      const veryOldTimestamp = Math.floor(now.getTime() / 1000) - 3600; // 1 hour ago

      expect(service.isOnline(veryOldTimestamp, now)).toBe(false);
    });

    it('should return true for heartbeat just now', () => {
      const now = new Date();
      const justNow = Math.floor(now.getTime() / 1000);

      expect(service.isOnline(justNow, now)).toBe(true);
    });

    it('should use current time when no time provided', () => {
      const justNow = Math.floor(Date.now() / 1000);

      expect(service.isOnline(justNow)).toBe(true);
    });
  });

  describe('getOnlineThreshold', () => {
    it('should return correct threshold timestamp', () => {
      const now = new Date('2025-01-01T12:00:00.000Z');
      const threshold = service.getOnlineThreshold(now);

      const expectedTimestamp = Math.floor(now.getTime() / 1000) - TimeWindow.DEFAULT_ONLINE_WINDOW_SECONDS;
      expect(threshold).toBe(expectedTimestamp);
    });

    it('should use current time when no time provided', () => {
      const beforeCall = Math.floor(Date.now() / 1000) - TimeWindow.DEFAULT_ONLINE_WINDOW_SECONDS;
      const threshold = service.getOnlineThreshold();
      const afterCall = Math.floor(Date.now() / 1000) - TimeWindow.DEFAULT_ONLINE_WINDOW_SECONDS;

      expect(threshold).toBeGreaterThanOrEqual(beforeCall);
      expect(threshold).toBeLessThanOrEqual(afterCall);
    });
  });

  describe('getWindowSeconds', () => {
    it('should return default window seconds', () => {
      expect(service.getWindowSeconds()).toBe(TimeWindow.DEFAULT_ONLINE_WINDOW_SECONDS);
    });

    it('should return 180 seconds', () => {
      expect(service.getWindowSeconds()).toBe(180);
    });
  });

  describe('integration scenarios', () => {
    it('should correctly determine online status for multiple users', () => {
      const now = new Date();
      const nowTimestamp = Math.floor(now.getTime() / 1000);

      const users = [
        { id: 1, lastHeartbeat: nowTimestamp - 30, expectedOnline: true },  // 30s ago
        { id: 2, lastHeartbeat: nowTimestamp - 90, expectedOnline: true },  // 90s ago
        { id: 3, lastHeartbeat: nowTimestamp - 150, expectedOnline: true }, // 150s ago
        { id: 4, lastHeartbeat: nowTimestamp - 180, expectedOnline: false }, // 180s ago (boundary)
        { id: 5, lastHeartbeat: nowTimestamp - 200, expectedOnline: false }, // 200s ago
      ];

      users.forEach((user) => {
        expect(service.isOnline(user.lastHeartbeat, now)).toBe(user.expectedOnline);
      });
    });

    it('should work correctly with getOnlineThreshold', () => {
      const now = new Date();
      const threshold = service.getOnlineThreshold(now);

      // Users with timestamp > threshold should be online
      expect(service.isOnline(threshold + 1, now)).toBe(true);

      // Users with timestamp <= threshold should be offline
      expect(service.isOnline(threshold, now)).toBe(false);
      expect(service.isOnline(threshold - 1, now)).toBe(false);
    });
  });
});
