import { BusinessTripSyncService } from '../../../../src/modules/organization/dingtalk/sync/business-trip-sync.service';
import { FieldApplicationSyncService } from '../../../../src/modules/organization/dingtalk/sync/field-application-sync.service';
import { LeaveExtensionSyncService } from '../../../../src/modules/organization/dingtalk/sync/leave-extension-sync.service';
import { LeaveReminderService } from '../../../../src/modules/organization/dingtalk/sync/leave-reminder.service';
import {
  BUSINESS_TRIP_FIELDS,
  BUSINESS_TRIP_CHANGE_FIELDS,
  BUSINESS_TRIP_CHANGE_TYPES,
  FIELD_APPLICATION_FIELDS,
  FORM_UUIDS,
  LEAVE_EXTENSION_DATA_FIELDS,
  LEAVE_EXTENSION_PROCESS_FIELDS,
} from '../../../../src/modules/organization/dingtalk/constants';

describe('Dingtalk sync services', () => {
  describe('BusinessTripSyncService', () => {
    it('应在跳过历史行程后继续递增 approveId 编号', async () => {
      const now = Date.now();
      const oldTs = now - 1000 * 60 * 60 * 24 * 120;
      const recentTs = now - 1000 * 60 * 60 * 24;
      const yidaService = {
        searchApprovedInstances: jest.fn().mockResolvedValue([
          {
            formData: {
              [BUSINESS_TRIP_FIELDS.TABLE_FIELD]: [
                {
                  [BUSINESS_TRIP_FIELDS.FROM_DATE]: oldTs,
                  [BUSINESS_TRIP_FIELDS.TO_DATE]: oldTs,
                  [BUSINESS_TRIP_FIELDS.FROM_SLOT]: '上午',
                  [BUSINESS_TRIP_FIELDS.TO_SLOT]: '上午',
                },
                {
                  [BUSINESS_TRIP_FIELDS.FROM_DATE]: recentTs,
                  [BUSINESS_TRIP_FIELDS.TO_DATE]: recentTs,
                  [BUSINESS_TRIP_FIELDS.FROM_SLOT]: '上午',
                  [BUSINESS_TRIP_FIELDS.TO_SLOT]: '下午',
                  [BUSINESS_TRIP_FIELDS.CREATOR]: '张三',
                },
              ],
              [BUSINESS_TRIP_FIELDS.CREATOR]: '张三',
            },
            creatorUserId: 'user001',
            serialNo: 'SN001',
            formInstanceId: 'INST001',
          },
        ]),
      };
      const attendanceService = {
        approveFinish: jest.fn().mockResolvedValue({ errcode: 0 }),
        getApproveRecords: jest.fn().mockResolvedValue({ errcode: 0, result: { approve_list: [{ tag_name: '外出' }] } }),
      };
      const authService = { appType: 'APP_TEST' };

      const service = new BusinessTripSyncService(
        yidaService as any,
        attendanceService as any,
        authService as any,
      );

      await service.sync();

      expect(attendanceService.approveFinish).toHaveBeenCalledTimes(1);
      expect(attendanceService.approveFinish).toHaveBeenCalledWith(
        expect.objectContaining({
          approveId: 'SN001-2',
        }),
      );
    });

    it('出差同步单条失败后后续行程仍应继续递增 approveId', async () => {
      const now = Date.now();
      const yidaService = {
        searchApprovedInstances: jest.fn().mockResolvedValue([
          {
            formData: {
              [BUSINESS_TRIP_FIELDS.TABLE_FIELD]: [
                {
                  [BUSINESS_TRIP_FIELDS.FROM_DATE]: now,
                  [BUSINESS_TRIP_FIELDS.TO_DATE]: now,
                  [BUSINESS_TRIP_FIELDS.FROM_SLOT]: '上午',
                  [BUSINESS_TRIP_FIELDS.TO_SLOT]: '下午',
                },
                {
                  [BUSINESS_TRIP_FIELDS.FROM_DATE]: now + 1000,
                  [BUSINESS_TRIP_FIELDS.TO_DATE]: now + 1000,
                  [BUSINESS_TRIP_FIELDS.FROM_SLOT]: '上午',
                  [BUSINESS_TRIP_FIELDS.TO_SLOT]: '下午',
                },
              ],
              [BUSINESS_TRIP_FIELDS.CREATOR]: '张三',
            },
            creatorUserId: 'user001',
            serialNo: 'SNERR',
            formInstanceId: 'INST_ERR_1',
          },
        ]),
      };
      const attendanceService = {
        approveFinish: jest.fn()
          .mockRejectedValueOnce(new Error('network'))
          .mockResolvedValueOnce({ errcode: 0 }),
        getApproveRecords: jest.fn().mockResolvedValue({ errcode: 0, result: { approve_list: [{ tag_name: '出差' }] } }),
      };

      const service = new BusinessTripSyncService(
        yidaService as any,
        attendanceService as any,
        { appType: 'APP_TEST' } as any,
      );

      await service.sync();

      expect(attendanceService.approveFinish).toHaveBeenNthCalledWith(
        1,
        expect.objectContaining({ approveId: 'SNERR-1' }),
      );
      expect(attendanceService.approveFinish).toHaveBeenNthCalledWith(
        2,
        expect.objectContaining({ approveId: 'SNERR-2' }),
      );
    });

    it('出差变更单条失败后后续新行程仍应继续递增 approveId', async () => {
      const now = Date.now();
      const yidaService = {
        searchApprovedInstances: jest.fn()
          .mockResolvedValueOnce([])
          .mockResolvedValueOnce([
            {
              formData: {
                [BUSINESS_TRIP_CHANGE_FIELDS.TYPE]: BUSINESS_TRIP_CHANGE_TYPES.FIRST_CHANGE,
                [BUSINESS_TRIP_CHANGE_FIELDS.FIRST_CHANGE_ASSOC]: JSON.stringify(JSON.stringify([{ title: 'PRE001', instanceId: 'PRE_INST_1' }])),
                [BUSINESS_TRIP_CHANGE_FIELDS.TABLE_FIELD]: [
                  {
                    [BUSINESS_TRIP_CHANGE_FIELDS.FROM_DATE]: now,
                    [BUSINESS_TRIP_CHANGE_FIELDS.TO_DATE]: now,
                    [BUSINESS_TRIP_CHANGE_FIELDS.FROM_SLOT]: '上午',
                    [BUSINESS_TRIP_CHANGE_FIELDS.TO_SLOT]: '下午',
                  },
                  {
                    [BUSINESS_TRIP_CHANGE_FIELDS.FROM_DATE]: now + 1000,
                    [BUSINESS_TRIP_CHANGE_FIELDS.TO_DATE]: now + 1000,
                    [BUSINESS_TRIP_CHANGE_FIELDS.FROM_SLOT]: '上午',
                    [BUSINESS_TRIP_CHANGE_FIELDS.TO_SLOT]: '下午',
                  },
                ],
                [BUSINESS_TRIP_CHANGE_FIELDS.CREATOR]: '李四',
              },
              creatorUserId: 'user002',
              serialNo: 'CHG001',
              formInstanceId: 'CHG_INST_1',
            },
          ]),
        getInstanceById: jest.fn().mockResolvedValue({
          formData: {
            [BUSINESS_TRIP_CHANGE_FIELDS.TABLE_FIELD]: [],
          },
        }),
      };
      const attendanceService = {
        approveCancel: jest.fn(),
        approveFinish: jest.fn()
          .mockRejectedValueOnce(new Error('network'))
          .mockResolvedValueOnce({ errcode: 0 }),
        getApproveRecords: jest.fn().mockResolvedValue({ errcode: 0, result: { approve_list: [{ tag_name: '出差' }] } }),
      };

      const service = new BusinessTripSyncService(
        yidaService as any,
        attendanceService as any,
        { appType: 'APP_TEST' } as any,
      );

      await service.syncChanges();

      expect(attendanceService.approveFinish).toHaveBeenNthCalledWith(
        1,
        expect.objectContaining({ approveId: 'CHG001-1' }),
      );
      expect(attendanceService.approveFinish).toHaveBeenNthCalledWith(
        2,
        expect.objectContaining({ approveId: 'CHG001-2' }),
      );
    });
  });

  describe('FieldApplicationSyncService', () => {
    it('应兼容 serialNumber 字段生成 approveId', async () => {
      const now = Date.now();
      const yidaService = {
        searchApprovedInstances: jest.fn().mockResolvedValue([
          {
            serialNumber: 'FIELD001',
            formInstanceId: 'FIELD_INST_1',
            creatorUserId: 'user010',
            formData: {
              [FIELD_APPLICATION_FIELDS.CREATOR]: '赵六',
              [FIELD_APPLICATION_FIELDS.TABLE_FIELD]: [
                {
                  [FIELD_APPLICATION_FIELDS.FROM_DATE]: now,
                  [FIELD_APPLICATION_FIELDS.TO_DATE]: now,
                },
              ],
            },
          },
        ]),
      };
      const attendanceService = {
        approveFinish: jest.fn().mockResolvedValue({ errcode: 0 }),
        getApproveRecords: jest.fn().mockResolvedValue({ errcode: 0, result: { approve_list: [{ tag_name: '外出' }] } }),
      };
      const authService = { appType: 'APP_TEST' };
      const service = new FieldApplicationSyncService(
        yidaService as any,
        attendanceService as any,
        authService as any,
      );

      await service.sync('2026-01-01', '2026-12-31');

      expect(attendanceService.approveFinish).toHaveBeenCalledWith(
        expect.objectContaining({
          approveId: 'FIELD001-1',
        }),
      );
    });
  });

  describe('LeaveExtensionSyncService', () => {
    it('应按北京时间写入假期延期显示时间字符串', async () => {
      const yidaService = {
        searchApprovedInstances: jest.fn().mockResolvedValue([
          {
            creatorUserId: 'user002',
            formData: {
              [LEAVE_EXTENSION_PROCESS_FIELDS.LEAVE_TYPE]: '2025年年假',
              [LEAVE_EXTENSION_PROCESS_FIELDS.EXPIRATION_DATE]: ['1706745600000', '1706831999000'],
              [LEAVE_EXTENSION_PROCESS_FIELDS.APPLICATION_DATE]: ['1704067200000', '1704153599000'],
              [LEAVE_EXTENSION_PROCESS_FIELDS.NAME]: '李四',
              [LEAVE_EXTENSION_PROCESS_FIELDS.EMPLOYEE_ID]: 'E002',
            },
          },
        ]),
        createOrUpdateForm: jest.fn().mockResolvedValue({ success: true }),
      };
      const attendanceService = {
        searchLeaveQuota: jest.fn().mockResolvedValue([
          {
            end_time: '1706745600000',
            quota_num_per_day: '200',
            used_num_per_day: '0',
            leave_code: '22f7226b-9aa2-4e14-a30d-8edee7064168',
            quota_id: 'quota001',
          },
        ]),
      };
      const service = new LeaveExtensionSyncService(yidaService as any, attendanceService as any);

      await service.sync();

      const [, , formDataJson] = (yidaService.createOrUpdateForm as jest.Mock).mock.calls[0];
      const formData = JSON.parse(formDataJson);

      expect(formData[LEAVE_EXTENSION_DATA_FIELDS.EXPIRATION_DATE_STR]).toMatch(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/);
      expect(formData[LEAVE_EXTENSION_DATA_FIELDS.APPLICATION_DATE_STR]).toMatch(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}-\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/);
      expect(formData[LEAVE_EXTENSION_DATA_FIELDS.EXPIRATION_DATE_STR]).not.toContain('T');
      expect(formData[LEAVE_EXTENSION_DATA_FIELDS.APPLICATION_DATE_STR]).not.toContain('T');
      expect(yidaService.createOrUpdateForm).toHaveBeenCalledWith(
        FORM_UUIDS.LEAVE_EXTENSION_DATA,
        expect.any(String),
        expect.any(String),
      );
    });
  });

  describe('LeaveReminderService', () => {
    it('延期天数小于等于 0 时不应回写为已通知', async () => {
      const yidaService = {
        searchForm: jest.fn().mockResolvedValue([
          {
            formData: {
              [LEAVE_EXTENSION_DATA_FIELDS.IS_NOTIFY]: 'No',
              [LEAVE_EXTENSION_DATA_FIELDS.EXPIRATION_DATE]: String(Date.now()),
              [LEAVE_EXTENSION_DATA_FIELDS.LEAVE_TYPE]: '2025年年假',
              [LEAVE_EXTENSION_DATA_FIELDS.USER_ID]: 'user003',
              [LEAVE_EXTENSION_DATA_FIELDS.DAYS]: '1',
              [LEAVE_EXTENSION_DATA_FIELDS.LEAVE_ID]: 'leave001',
              [LEAVE_EXTENSION_DATA_FIELDS.EMPLOYEE_NAME]: '王五',
            },
          },
        ]),
        createOrUpdateForm: jest.fn(),
      };
      const attendanceService = {
        getLeaveRecords: jest.fn().mockResolvedValue([
          { calType: '', leaveRecordType: 'leave', leaveStatus: 'approved', recordNumPerDay: 200 },
        ]),
      };
      const service = new LeaveReminderService(yidaService as any, attendanceService as any);

      await service.sync();

      expect(yidaService.createOrUpdateForm).not.toHaveBeenCalled();
    });
  });
});
