import { AnnualLeaveInsightService } from '../../../../src/modules/organization/dingtalk/annual-leave-insight.service';
import {
  EMPLOYEE_INFO_FIELDS,
  LEAVE_CODES,
} from '../../../../src/modules/organization/dingtalk/constants';

describe('AnnualLeaveInsightService', () => {
  it('应刷新余额快照并写入本地表', async () => {
    const prisma = {
      $transaction: jest.fn(async (callback) =>
        callback({
          dingtalkLeaveQuotaSnapshot: {
            deleteMany: jest.fn().mockResolvedValue({ count: 0 }),
            createMany: jest.fn().mockResolvedValue({ count: 1 }),
          },
        }),
      ),
    };
    const attendanceService = {
      searchLeaveQuota: jest.fn().mockImplementation(async (leaveCode) => {
        if (leaveCode === LEAVE_CODES.ANNUAL_LEAVE_2025) {
          return [
            {
              userid: 'user001',
              quota_cycle: '2026',
              quota_num_per_day: 1000,
              used_num_per_day: 400,
              start_time: new Date('2026-01-01T00:00:00+08:00').getTime(),
              end_time: new Date('2027-03-31T23:59:59+08:00').getTime(),
            },
          ];
        }
        return [];
      }),
    };
    const hrmService = {
      getOnJobEmployeeIds: jest.fn().mockResolvedValue(['user001']),
      getEmployeeInfoByIds: jest.fn().mockResolvedValue({
        user001: {
          field_data_list: [
            { field_name: '姓名', field_value_list: [{ value: '张三' }] },
            { field_name: '工号', field_value_list: [{ value: 'E001' }] },
          ],
        },
      }),
    };
    const service = new AnnualLeaveInsightService(
      prisma as any,
      attendanceService as any,
      hrmService as any,
      {} as any,
    );
    const result = await service.refreshQuotaSnapshot();

    expect(result.employeeCount).toBe(1);
    expect(result.recordCount).toBe(1);
    expect(result.leaveTypeCount).toBe(1);
    expect(prisma.$transaction).toHaveBeenCalled();
  });

  it('应从本地快照读取余额总览并计算统计', async () => {
    const prisma = {
      dingtalkLeaveQuotaSnapshot: {
        findMany: jest.fn().mockResolvedValue([
          {
            userId: 'user001',
            employeeName: '张三',
            employeeNumber: 'E001',
            leaveType: '2025年年假',
            leaveCode: LEAVE_CODES.ANNUAL_LEAVE_2025,
            quotaCycle: '2026',
            totalDays: 10,
            usedDays: 4,
            remainingDays: 6,
            startDate: new Date('2026-01-01'),
            endDate: new Date('2027-03-31'),
            snapshotAt: new Date('2026-03-11T04:00:00Z'),
          },
        ]),
        findFirst: jest.fn().mockResolvedValue({
          snapshotAt: new Date('2026-03-11T04:00:00Z'),
        }),
      },
    };

    const service = new AnnualLeaveInsightService(
      prisma as any,
      {} as any,
      {} as any,
      {} as any,
    );
    const result = await service.getQuotaOverview({ hideZero: true });

    expect(result.lastSyncedAt).toBe('2026-03-11T04:00:00.000Z');
    expect(result.summary.employeeCount).toBe(1);
    expect(result.items[0]).toMatchObject({
      userId: 'user001',
      totalDays: 10,
      usedDays: 4,
      remainingDays: 6,
    });
  });

  it('应从年假释放表展开未来释放计划并聚合同日天数', async () => {
    const prisma = {};
    const attendanceService = {};
    const hrmService = {};
    const prismaRepo = {
      dingtalkAnnualLeaveReleasePlan: {
        findMany: jest.fn().mockResolvedValue([
          {
            userId: 'user001',
            employeeName: '张三',
            employeeNumber: 'E001',
            status: '正常',
            year: 2026,
            releaseSchedule: [
              { dayIndex: 1, releaseDate: '2099-04-01' },
              { dayIndex: 2, releaseDate: '2099-04-01' },
              { dayIndex: 3, releaseDate: '2099-07-01' },
            ],
          },
        ]),
      },
    };
    const yidaService = {
      searchForm: jest.fn().mockResolvedValue([
        {
          formData: {
            [EMPLOYEE_INFO_FIELDS.USER_ID]: 'user001',
            [EMPLOYEE_INFO_FIELDS.NAME]: '张三',
            [EMPLOYEE_INFO_FIELDS.NUMBER]: 'E001',
            [EMPLOYEE_INFO_FIELDS.STATUS]: '正常',
            [EMPLOYEE_INFO_FIELDS.START_DATE]: '2024-01-01',
            [EMPLOYEE_INFO_FIELDS.WORK_DATE]: '2020-01-01',
          },
        },
      ]),
    };

    const service = new AnnualLeaveInsightService(
      prismaRepo as any,
      attendanceService as any,
      hrmService as any,
      yidaService as any,
    );
    const result = await service.getReleasePlan({
      year: 2026,
      upcomingOnly: true,
      includeConsultant: true,
    });

    expect(result.summary.employeeCount).toBe(1);
    expect(result.summary.planCount).toBe(2);
    expect(result.summary.upcomingDays).toBe(3);
    expect(result.items[0]).toMatchObject({
      date: '2099-04-01',
      userId: 'user001',
      days: 2,
      released: false,
      hasPlan: true,
    });
  });

  it('应返回无释放计划员工并标注原因', async () => {
    const prismaRepo = {
      dingtalkAnnualLeaveReleasePlan: {
        findMany: jest.fn().mockResolvedValue([]),
      },
    };
    const yidaService = {
      searchForm: jest.fn().mockResolvedValue([
        {
          formData: {
            [EMPLOYEE_INFO_FIELDS.USER_ID]: 'user002',
            [EMPLOYEE_INFO_FIELDS.NAME]: '李四',
            [EMPLOYEE_INFO_FIELDS.NUMBER]: 'E002',
            [EMPLOYEE_INFO_FIELDS.STATUS]: '正常',
            [EMPLOYEE_INFO_FIELDS.START_DATE]: '',
            [EMPLOYEE_INFO_FIELDS.WORK_DATE]: '',
          },
        },
      ]),
    };

    const service = new AnnualLeaveInsightService(
      prismaRepo as any,
      {} as any,
      {} as any,
      yidaService as any,
    );

    const result = await service.getReleasePlan({
      year: 2026,
      upcomingOnly: true,
      includeConsultant: true,
    });

    expect(result.summary.employeeCount).toBe(1);
    expect(result.summary.planCount).toBe(0);
    expect(result.items[0]).toMatchObject({
      userId: 'user002',
      hasPlan: false,
      noPlanReason: '缺少入职日期和首次参加工作日期',
    });
  });

  it('顾问状态应展示为不生成计划原因', async () => {
    const prismaRepo = {
      dingtalkAnnualLeaveReleasePlan: {
        findMany: jest.fn().mockResolvedValue([
          {
            userId: 'user003',
            employeeName: '王五',
            employeeNumber: 'E003',
            status: '顾问',
            year: 2026,
            totalDays: 0,
            releaseSchedule: [],
          },
        ]),
      },
    };
    const yidaService = {
      searchForm: jest.fn().mockResolvedValue([
        {
          formData: {
            [EMPLOYEE_INFO_FIELDS.USER_ID]: 'user003',
            [EMPLOYEE_INFO_FIELDS.NAME]: '王五',
            [EMPLOYEE_INFO_FIELDS.NUMBER]: 'E003',
            [EMPLOYEE_INFO_FIELDS.STATUS]: '正常',
            [EMPLOYEE_INFO_FIELDS.START_DATE]: '2024-01-01',
            [EMPLOYEE_INFO_FIELDS.WORK_DATE]: '2020-01-01',
          },
        },
      ]),
    };

    const service = new AnnualLeaveInsightService(
      prismaRepo as any,
      {} as any,
      {} as any,
      yidaService as any,
    );

    const result = await service.getReleasePlan({
      year: 2026,
      upcomingOnly: true,
      includeConsultant: true,
    });

    expect(result.items[0]).toMatchObject({
      userId: 'user003',
      status: '顾问',
      hasPlan: false,
      noPlanReason: '员工状态为顾问，本年度不生成释放计划',
    });
  });

  it('默认应隐藏顾问、停薪留职和已离职员工', async () => {
    const prismaRepo = {
      dingtalkAnnualLeaveReleasePlan: {
        findMany: jest.fn().mockResolvedValue([
          {
            userId: 'user-normal',
            employeeName: '正常员工',
            employeeNumber: 'E100',
            status: '正常',
            year: 2026,
            totalDays: 1,
            releaseSchedule: [{ dayIndex: 1, releaseDate: '2099-04-01' }],
          },
          {
            userId: 'user-consultant',
            employeeName: '顾问员工',
            employeeNumber: 'E101',
            status: '顾问',
            year: 2026,
            totalDays: 0,
            releaseSchedule: [],
          },
          {
            userId: 'user-leave',
            employeeName: '停薪留职员工',
            employeeNumber: 'E102',
            status: '停薪留职',
            year: 2026,
            totalDays: 0,
            releaseSchedule: [],
          },
          {
            userId: 'user-term',
            employeeName: '已离职员工',
            employeeNumber: 'E103',
            status: '已离职',
            year: 2026,
            totalDays: 0,
            releaseSchedule: [],
          },
        ]),
      },
    };
    const yidaService = {
      searchForm: jest.fn().mockResolvedValue([]),
    };

    const service = new AnnualLeaveInsightService(
      prismaRepo as any,
      {} as any,
      {} as any,
      yidaService as any,
    );

    const result = await service.getReleasePlan({ year: 2026, upcomingOnly: true });

    expect(result.items).toHaveLength(1);
    expect(result.items[0].userId).toBe('user-normal');
  });

  it('开启状态开关后应返回对应特殊状态员工', async () => {
    const prismaRepo = {
      dingtalkAnnualLeaveReleasePlan: {
        findMany: jest.fn().mockResolvedValue([
          {
            userId: 'user-consultant',
            employeeName: '顾问员工',
            employeeNumber: 'E101',
            status: '顾问',
            year: 2026,
            totalDays: 0,
            releaseSchedule: [],
          },
          {
            userId: 'user-leave',
            employeeName: '停薪留职员工',
            employeeNumber: 'E102',
            status: '停薪留职',
            year: 2026,
            totalDays: 0,
            releaseSchedule: [],
          },
          {
            userId: 'user-term',
            employeeName: '已离职员工',
            employeeNumber: 'E103',
            status: '已离职',
            year: 2026,
            totalDays: 0,
            releaseSchedule: [],
          },
        ]),
      },
    };
    const yidaService = {
      searchForm: jest.fn().mockResolvedValue([]),
    };

    const service = new AnnualLeaveInsightService(
      prismaRepo as any,
      {} as any,
      {} as any,
      yidaService as any,
    );

    const result = await service.getReleasePlan({
      year: 2026,
      upcomingOnly: true,
      includeConsultant: true,
      includeLeaveWithoutPay: true,
      includeTerminated: true,
    });

    expect(result.items.map(item => item.userId)).toEqual([
      'user-consultant',
      'user-leave',
      'user-term',
    ]);
  });

  it('应返回指定假期类型的释放记录与使用记录详情', async () => {
    const prisma = {
      dingtalkLeaveQuotaSnapshot: {
        findFirst: jest.fn().mockResolvedValue({
          userId: 'user001',
          employeeName: '张三',
          employeeNumber: 'E001',
          leaveType: '2025年年假',
          leaveCode: LEAVE_CODES.ANNUAL_LEAVE_2025,
          quotaCycle: '2026',
          totalDays: 10,
          usedDays: 4,
          remainingDays: 6,
          startDate: new Date('2026-01-01'),
          endDate: new Date('2027-03-31'),
        }),
      },
    };
    const attendanceService = {
      getLeaveRecords: jest.fn().mockResolvedValue([
        {
          start_time: new Date('2026-02-03T09:00:00+08:00').getTime(),
          end_time: new Date('2026-02-03T18:00:00+08:00').getTime(),
          record_num_per_day: 100,
          leave_status: 'approved',
          leave_record_type: 'consume',
        },
      ]),
    };
    const prismaRepo = {
      dingtalkLeaveQuotaSnapshot: {
        findFirst: jest.fn().mockResolvedValue({
          userId: 'user001',
          employeeName: '张三',
          employeeNumber: 'E001',
          leaveType: '2025年年假',
          leaveCode: LEAVE_CODES.ANNUAL_LEAVE_2025,
          quotaCycle: '2026',
          totalDays: 10,
          usedDays: 4,
          remainingDays: 6,
          startDate: new Date('2026-01-01'),
          endDate: new Date('2027-03-31'),
        }),
      },
      dingtalkAnnualLeaveReleasePlan: {
        findMany: jest.fn().mockResolvedValue([
          {
            userId: 'user001',
            employeeName: '张三',
            employeeNumber: 'E001',
            status: '正常',
            year: 2025,
            releaseSchedule: [
              { dayIndex: 1, releaseDate: '2026-04-01' },
              { dayIndex: 2, releaseDate: '2026-04-01' },
              { dayIndex: 3, releaseDate: '2026-07-01' },
            ],
          },
        ]),
      },
    };

    const service = new AnnualLeaveInsightService(
      prismaRepo as any,
      attendanceService as any,
      {} as any,
      {} as any,
    );

    const result = await service.getQuotaDetail({
      userId: 'user001',
      leaveCode: LEAVE_CODES.ANNUAL_LEAVE_2025,
    });

    expect(result).not.toBeNull();
    expect(result?.usageRecords).toHaveLength(1);
    expect(result?.usageRecords[0]).toMatchObject({
      date: '2026-02-03',
      days: 1,
      status: 'approved',
    });
    expect(result?.releaseRecords).toHaveLength(2);
    expect(result?.releaseRecords[0]).toMatchObject({
      date: '2026-04-01',
      days: 2,
      year: '2025',
    });
  });
});
