import { DashboardService } from './dashboard.service';

const createQb = () => ({
  leftJoin: jest.fn().mockReturnThis(),
  select: jest.fn().mockReturnThis(),
  addSelect: jest.fn().mockReturnThis(),
  where: jest.fn().mockReturnThis(),
  andWhere: jest.fn().mockReturnThis(),
  groupBy: jest.fn().mockReturnThis(),
  addGroupBy: jest.fn().mockReturnThis(),
  orderBy: jest.fn().mockReturnThis(),
  limit: jest.fn().mockReturnThis(),
  getRawMany: jest.fn().mockResolvedValue([]),
  getRawOne: jest.fn().mockResolvedValue({ cnt: '0' }),
});

describe('DashboardService — teacher class scope', () => {
  it('filters class attendance ranking by assigned classes', async () => {
    const qb = createQb();
    const attendanceRepo = { createQueryBuilder: jest.fn().mockReturnValue(qb) };
    const service = new DashboardService(
      {} as never,
      {} as never,
      {} as never,
      {} as never,
      {} as never,
      {} as never,
      {} as never,
      attendanceRepo as never,
      {} as never,
      {} as never,
      {} as never,
      {} as never,
      {} as never,
      {},
    );

    await service.getClassAttendanceRanking([8, 9]);

    expect(qb.andWhere).toHaveBeenCalledWith('a.classId IN (:...accessibleClassIds)', {
      accessibleClassIds: [8, 9],
    });
  });
});

describe('DashboardService — boundary conditions', () => {
  it('uses a deny-all predicate instead of an empty SQL IN list', async () => {
    const qb = createQb();
    const attendanceRepo = { createQueryBuilder: jest.fn().mockReturnValue(qb) };
    const service = new DashboardService(
      {} as never, {} as never, {} as never, {} as never, {} as never, {} as never,
      {} as never, attendanceRepo as never, {} as never, {} as never, {} as never,
      {} as never, {} as never,
    );

    await (service as unknown as {
      getAttendanceTrend: (today: string, classIds: number[]) => Promise<unknown>;
    }).getAttendanceTrend('2026-07-14', []);

    expect(qb.andWhere).toHaveBeenCalledWith('1 = 0');
  });

  it.each([
    ['getGanttData', [{ periodStart: '2026-08-01', periodEnd: '2026-07-31' }]],
    ['getExpenseStats', ['2026-08-01', '2026-07-31']],
    ['getRoomExpenseRanking', ['2026-08-01', '2026-07-31']],
  ] as const)('rejects a reversed period in %s', async (method, args) => {
    const service = new DashboardService(
      {} as never, {} as never, {} as never, {} as never, {} as never, {} as never,
      {} as never, {} as never, {} as never, {} as never, {} as never, {} as never,
      {} as never,
    );
    await expect((service[method] as (...values: never[]) => Promise<unknown>)(...(args as never[])))
      .rejects.toThrow('结束日期不能早于开始日期');
  });

  it('uses the China calendar date when the server timezone is behind China', () => {
    const service = new DashboardService(
      {} as never, {} as never, {} as never, {} as never, {} as never, {} as never,
      {} as never, {} as never, {} as never, {} as never, {} as never, {} as never,
      {} as never,
    );
    expect((service as unknown as { getChinaDate: (date: Date) => string })
      .getChinaDate(new Date('2026-07-13T16:30:00.000Z'))).toBe('2026-07-14');
  });
});
