import { ZoomFinalSessionAttendanceService } from './zoom-final-session-attendance.service';
import { AttendanceStatus } from 'src/common/enum/attendance-status.enum';

describe('ZoomFinalSessionAttendanceService', () => {
  const past = (daysAgo: number) => new Date(Date.now() - daysAgo * 24 * 60 * 60 * 1000);
  const future = (daysAhead: number) => new Date(Date.now() + daysAhead * 24 * 60 * 60 * 1000);

  const session = (id: number, startsAt: Date, onlineSessionId: number | null = 100 + id) => ({
    id,
    programId: 1,
    startsAt,
    displayOrder: 1,
    onlineSession: onlineSessionId != null ? { id: onlineSessionId } : null,
  });

  const programSessionRepo = { find: jest.fn() };
  const onlineAttendanceService = { getManualMarksBySession: jest.fn() };

  let service: ZoomFinalSessionAttendanceService;

  beforeEach(() => {
    jest.clearAllMocks();
    service = new ZoomFinalSessionAttendanceService(
      programSessionRepo as any,
      onlineAttendanceService as any,
    );
  });

  describe('getFinalSessionId', () => {
    it('returns null for a single-session program', async () => {
      programSessionRepo.find.mockResolvedValue([session(1, past(1))]);

      expect(await service.getFinalSessionId(1)).toBeNull();
    });

    it("returns the last session's id (by startsAt) for a multi-session program", async () => {
      programSessionRepo.find.mockResolvedValue([session(1, past(2)), session(2, past(1)), session(3, future(1))]);

      expect(await service.getFinalSessionId(1)).toBe(3);
    });
  });

  describe('resolveFinalSessionAbsentees', () => {
    it('returns null for a single-session program (no final-session concept)', async () => {
      programSessionRepo.find.mockResolvedValue([session(1, past(1))]);

      const result = await service.resolveFinalSessionAbsentees(1, [10, 11]);

      expect(result).toBeNull();
      expect(onlineAttendanceService.getManualMarksBySession).not.toHaveBeenCalled();
    });

    it('flags a candidate absent from any already-elapsed earlier session, leaves an all-present candidate out', async () => {
      programSessionRepo.find.mockResolvedValue([session(1, past(2)), session(2, past(1)), session(3, future(1))]);
      onlineAttendanceService.getManualMarksBySession.mockImplementation((sessionId: number) => {
        if (sessionId === 1) {
          return Promise.resolve([
            { registrationId: 10, attendanceStatus: AttendanceStatus.ABSENT },
            { registrationId: 11, attendanceStatus: AttendanceStatus.PRESENT },
          ]);
        }
        return Promise.resolve([
          { registrationId: 10, attendanceStatus: AttendanceStatus.PRESENT },
          { registrationId: 11, attendanceStatus: AttendanceStatus.PRESENT },
        ]);
      });

      const result = await service.resolveFinalSessionAbsentees(1, [10, 11]);

      expect(result).toEqual({ finalSessionId: 3, absenteeRegistrationIds: new Set([10]) });
      // Session 3 (the final session) is never queried for attendance.
      expect(onlineAttendanceService.getManualMarksBySession).not.toHaveBeenCalledWith(3);
    });

    it('excludes nobody when no earlier session has elapsed yet', async () => {
      programSessionRepo.find.mockResolvedValue([session(1, future(1)), session(2, future(2))]);

      const result = await service.resolveFinalSessionAbsentees(1, [10]);

      expect(result).toEqual({ finalSessionId: 2, absenteeRegistrationIds: new Set() });
      expect(onlineAttendanceService.getManualMarksBySession).not.toHaveBeenCalled();
    });

    it('treats a candidate with no attendance mark at all as absent', async () => {
      programSessionRepo.find.mockResolvedValue([session(1, past(1)), session(2, future(1))]);
      onlineAttendanceService.getManualMarksBySession.mockResolvedValue([]);

      const result = await service.resolveFinalSessionAbsentees(1, [10]);

      expect(result).toEqual({ finalSessionId: 2, absenteeRegistrationIds: new Set([10]) });
    });

    it('returns no absentees when given no candidates', async () => {
      programSessionRepo.find.mockResolvedValue([session(1, past(1)), session(2, future(1))]);

      const result = await service.resolveFinalSessionAbsentees(1, []);

      expect(result).toEqual({ finalSessionId: 2, absenteeRegistrationIds: new Set() });
      expect(onlineAttendanceService.getManualMarksBySession).not.toHaveBeenCalled();
    });
  });
});
