import { AttendanceLeaveSyncService } from './attendance-leave-sync.service';
import { DingTalkService } from '../integration/dingtalk.service';

describe('AttendanceLeaveSyncService', () => {
  const dingLeaveRawRepo = {
    find: jest.fn(),
    findOne: jest.fn(),
    create: jest.fn((value: Record<string, unknown>) => value),
    save: jest.fn(),
  };
  const studentRepo = { findOne: jest.fn() };
  const studentDingMappingRepo = { findOne: jest.fn(), find: jest.fn() };
  const dingTalkService = {
    fetchDailyLeaveStatus: jest.fn(),
  };

  let service: AttendanceLeaveSyncService;

  beforeEach(() => {
    jest.clearAllMocks();
    service = new AttendanceLeaveSyncService(
      dingLeaveRawRepo as never,
      studentRepo as never,
      studentDingMappingRepo as never,
      dingTalkService as unknown as DingTalkService,
    );
  });

  it('syncs approved DingTalk leaves per user per day and auto-matches them', async () => {
    dingTalkService.fetchDailyLeaveStatus.mockImplementation(
      async (userId: string, workDate: string) => [
        {
          userId,
          workDate,
          procInstId: `leave-${userId}-${workDate}`,
          tagName: '请假',
          leaveType: '事假',
          beginTime: new Date(`${workDate}T08:00:00+08:00`),
          endTime: new Date(`${workDate}T12:00:00+08:00`),
          approvedAt: new Date(`${workDate}T09:00:00+08:00`),
          duration: '0.5',
          durationUnit: 'day',
        },
      ],
    );
    dingLeaveRawRepo.findOne.mockResolvedValue(null);
    dingLeaveRawRepo.save.mockImplementation(async (entities) => entities);
    studentDingMappingRepo.find.mockResolvedValue([{ dingUserId: 'ding-1', studentId: 7 }]);
    dingLeaveRawRepo.find.mockResolvedValue([
      { dingId: 'leave-ding-1-2026-07-01', dingUserId: 'ding-1', matchStatus: 'unmatched' },
    ]);

    const result = await service.syncLeaveStatusForLesson({
      startDate: '2026-07-01',
      endDate: '2026-07-02',
      userIds: ['ding-1', 'ding-2'],
      autoMatch: true,
    });

    expect(dingTalkService.fetchDailyLeaveStatus).toHaveBeenCalledTimes(4);
    expect(dingTalkService.fetchDailyLeaveStatus).toHaveBeenCalledWith(
      'ding-1',
      '2026-07-01',
    );
    expect(dingTalkService.fetchDailyLeaveStatus).toHaveBeenCalledWith(
      'ding-2',
      '2026-07-02',
    );
    expect(dingLeaveRawRepo.save).toHaveBeenCalled();
    expect(result.synced).toBe(4);
    expect(result.matched).toBe(1);
  });

  it('keeps syncing remaining users when one leave fetch fails', async () => {
    dingTalkService.fetchDailyLeaveStatus
      .mockRejectedValueOnce(new Error('DingTalk unavailable'))
      .mockResolvedValue([]);
    dingLeaveRawRepo.findOne.mockResolvedValue(null);

    const result = await service.syncLeaveStatusForLesson({
      startDate: '2026-07-01',
      endDate: '2026-07-01',
      userIds: ['ding-1', 'ding-2'],
      autoMatch: false,
    });

    expect(dingTalkService.fetchDailyLeaveStatus).toHaveBeenCalledTimes(2);
    expect(result.errors).toHaveLength(1);
    expect(result.synced).toBe(0);
  });
});
