import { AnnualLeaveSyncService } from '../../../../src/modules/organization/dingtalk/sync/annual-leave-sync.service';
import { EMPLOYEE_INFO_FIELDS, FORM_UUIDS } from '../../../../src/modules/organization/dingtalk/constants';

describe('AnnualLeaveSyncService', () => {
  afterEach(() => {
    jest.useRealTimers();
    jest.restoreAllMocks();
  });

  it('应将年假释放计划写入本地数据库，并在命中释放日时更新钉钉额度', async () => {
    jest.useFakeTimers().setSystemTime(new Date('2026-04-01T12:00:00Z'));

    const planRepo = {
      findMany: jest.fn()
        .mockResolvedValueOnce([])
        .mockResolvedValueOnce([]),
      upsert: jest.fn().mockImplementation(async ({ create }) => create),
      update: jest.fn(),
    };
    const prisma = {
      dingtalkAnnualLeaveReleasePlan: planRepo,
    };
    const yidaService = {
      searchForm: jest.fn().mockImplementation(async (formUuid: string) => {
        if (formUuid === FORM_UUIDS.EMPLOYEE_INFO_WEEKLY) {
          return [
            {
              formData: {
                [EMPLOYEE_INFO_FIELDS.USER_ID]: 'user001',
                [EMPLOYEE_INFO_FIELDS.NUMBER]: 'E001',
                [EMPLOYEE_INFO_FIELDS.NAME]: '张三',
                [EMPLOYEE_INFO_FIELDS.START_DATE]: '2024-01-01',
                [EMPLOYEE_INFO_FIELDS.WORK_DATE]: '2020-01-01',
                [EMPLOYEE_INFO_FIELDS.STATUS]: '正常',
              },
            },
          ];
        }
        return [];
      }),
    };
    const attendanceService = {
      updateLeaveQuota: jest.fn().mockResolvedValue({ errcode: 0 }),
    };

    const service = new AnnualLeaveSyncService(
      prisma as any,
      yidaService as any,
      attendanceService as any,
    );

    jest.spyOn(service, 'calculateLeaveDates').mockReturnValue(['2026-04-01', '2026-07-01']);

    const result = await service.sync();

    expect(planRepo.upsert).toHaveBeenCalled();
    expect(planRepo.upsert.mock.calls[0][0].create).toMatchObject({
      userId: 'user001',
      employeeName: '张三',
      employeeNumber: 'E001',
      year: 2026,
      totalDays: 2,
      releaseSchedule: [
        { dayIndex: 1, releaseDate: '2026-04-01' },
        { dayIndex: 2, releaseDate: '2026-07-01' },
      ],
    });
    expect(attendanceService.updateLeaveQuota).toHaveBeenCalledWith([
      expect.objectContaining({
        userid: 'user001',
        quota_num_per_day: 100,
      }),
    ]);
    expect(result.success).toBe(true);
  });

  it('手动重算年假计划时只更新本地中间表，不发放钉钉额度', async () => {
    jest.useFakeTimers().setSystemTime(new Date('2026-04-01T12:00:00Z'));

    const planRepo = {
      findMany: jest.fn()
        .mockResolvedValueOnce([])
        .mockResolvedValueOnce([]),
      upsert: jest.fn().mockImplementation(async ({ create }) => create),
      update: jest.fn(),
    };
    const prisma = {
      dingtalkAnnualLeaveReleasePlan: planRepo,
    };
    const yidaService = {
      searchForm: jest.fn().mockResolvedValue([
        {
          formData: {
            [EMPLOYEE_INFO_FIELDS.USER_ID]: 'user001',
            [EMPLOYEE_INFO_FIELDS.NUMBER]: 'E001',
            [EMPLOYEE_INFO_FIELDS.NAME]: '张三',
            [EMPLOYEE_INFO_FIELDS.START_DATE]: '2024-01-01',
            [EMPLOYEE_INFO_FIELDS.WORK_DATE]: '2020-01-01',
            [EMPLOYEE_INFO_FIELDS.STATUS]: '正常',
          },
        },
      ]),
    };
    const attendanceService = {
      updateLeaveQuota: jest.fn().mockResolvedValue({ errcode: 0 }),
    };

    const service = new AnnualLeaveSyncService(
      prisma as any,
      yidaService as any,
      attendanceService as any,
    );

    jest.spyOn(service, 'calculateLeaveDates').mockReturnValue(['2026-04-01', '2026-07-01']);

    const result = await service.refreshPlan();

    expect(planRepo.upsert).toHaveBeenCalled();
    expect(attendanceService.updateLeaveQuota).not.toHaveBeenCalled();
    expect(result.success).toBe(true);
  });

  it('按指定年份重算时应写入目标年度计划', async () => {
    jest.useFakeTimers().setSystemTime(new Date('2026-04-01T12:00:00Z'));

    const planRepo = {
      findMany: jest.fn()
        .mockResolvedValueOnce([])
        .mockResolvedValueOnce([]),
      upsert: jest.fn().mockImplementation(async ({ create }) => create),
      update: jest.fn(),
    };
    const prisma = {
      dingtalkAnnualLeaveReleasePlan: planRepo,
    };
    const yidaService = {
      searchForm: jest.fn().mockResolvedValue([
        {
          formData: {
            [EMPLOYEE_INFO_FIELDS.USER_ID]: 'user001',
            [EMPLOYEE_INFO_FIELDS.NUMBER]: 'E001',
            [EMPLOYEE_INFO_FIELDS.NAME]: '张三',
            [EMPLOYEE_INFO_FIELDS.START_DATE]: '2024-01-01',
            [EMPLOYEE_INFO_FIELDS.WORK_DATE]: '2020-01-01',
            [EMPLOYEE_INFO_FIELDS.STATUS]: '正常',
          },
        },
      ]),
    };
    const attendanceService = {
      updateLeaveQuota: jest.fn().mockResolvedValue({ errcode: 0 }),
    };

    const service = new AnnualLeaveSyncService(
      prisma as any,
      yidaService as any,
      attendanceService as any,
    );

    jest.spyOn(service, 'calculateLeaveDates').mockReturnValue(['2025-03-01']);

    await service.refreshPlan('user001', 2025);

    expect(service.calculateLeaveDates).toHaveBeenCalledWith(
      '2024-01-01',
      '2020-01-01',
      0,
      2025,
    );
    expect(planRepo.upsert.mock.calls[0][0].create.year).toBe(2025);
    expect(attendanceService.updateLeaveQuota).not.toHaveBeenCalled();
  });

  it('缺少入职或工作日期时也应创建空计划记录', async () => {
    jest.useFakeTimers().setSystemTime(new Date('2026-04-01T12:00:00Z'));

    const planRepo = {
      findMany: jest.fn()
        .mockResolvedValueOnce([])
        .mockResolvedValueOnce([]),
      upsert: jest.fn().mockImplementation(async ({ create }) => create),
      update: jest.fn(),
    };
    const prisma = {
      dingtalkAnnualLeaveReleasePlan: planRepo,
    };
    const yidaService = {
      searchForm: jest.fn().mockResolvedValue([
        {
          formData: {
            [EMPLOYEE_INFO_FIELDS.USER_ID]: 'user002',
            [EMPLOYEE_INFO_FIELDS.NUMBER]: 'E002',
            [EMPLOYEE_INFO_FIELDS.NAME]: '李四',
            [EMPLOYEE_INFO_FIELDS.START_DATE]: '',
            [EMPLOYEE_INFO_FIELDS.WORK_DATE]: '2020-01-01',
            [EMPLOYEE_INFO_FIELDS.STATUS]: '正常',
          },
        },
      ]),
    };
    const attendanceService = {
      updateLeaveQuota: jest.fn().mockResolvedValue({ errcode: 0 }),
    };

    const service = new AnnualLeaveSyncService(
      prisma as any,
      yidaService as any,
      attendanceService as any,
    );

    const result = await service.refreshPlan();

    expect(planRepo.upsert).toHaveBeenCalled();
    expect(planRepo.upsert.mock.calls[0][0].create).toMatchObject({
      userId: 'user002',
      employeeName: '李四',
      year: 2026,
      totalDays: 0,
      releaseSchedule: [],
      joinDate: null,
    });
    expect(result.success).toBe(true);
  });

  it('状态为顾问时不应参与释放计划', async () => {
    jest.useFakeTimers().setSystemTime(new Date('2026-04-01T12:00:00Z'));

    const planRepo = {
      findMany: jest.fn()
        .mockResolvedValueOnce([
          {
            userId: 'user003',
            employeeName: '王五',
            employeeNumber: 'E003',
            year: 2026,
            status: '顾问',
            joinDate: new Date('2024-01-01'),
            workStartDate: new Date('2020-01-01'),
            adjustmentDays: 0,
            notCountDays: 0,
            totalDays: 1,
            releaseSchedule: [{ dayIndex: 1, releaseDate: '2026-04-01' }],
          },
        ])
        .mockResolvedValueOnce([]),
      upsert: jest.fn(),
      update: jest.fn(),
    };
    const prisma = {
      dingtalkAnnualLeaveReleasePlan: planRepo,
    };
    const yidaService = {
      searchForm: jest.fn().mockResolvedValue([
        {
          formData: {
            [EMPLOYEE_INFO_FIELDS.USER_ID]: 'user003',
            [EMPLOYEE_INFO_FIELDS.NUMBER]: 'E003',
            [EMPLOYEE_INFO_FIELDS.NAME]: '王五',
            [EMPLOYEE_INFO_FIELDS.START_DATE]: '2024-01-01',
            [EMPLOYEE_INFO_FIELDS.WORK_DATE]: '2020-01-01',
            [EMPLOYEE_INFO_FIELDS.STATUS]: '正常',
          },
        },
      ]),
    };
    const attendanceService = {
      updateLeaveQuota: jest.fn().mockResolvedValue({ errcode: 0 }),
    };

    const service = new AnnualLeaveSyncService(
      prisma as any,
      yidaService as any,
      attendanceService as any,
    );

    const result = await service.sync();

    expect(attendanceService.updateLeaveQuota).not.toHaveBeenCalled();
    expect(result.success).toBe(true);
  });
});
