import 'reflect-metadata';
import { validate } from 'class-validator';
import { CreateScheduleDto, UpdateScheduleDto } from './schedule.dto';

const createSchedule = (notes: string) =>
  Object.assign(new CreateScheduleDto(), {
    classId: 1,
    classroomId: 2,
    weekDay: 1,
    startTime: '09:00',
    endTime: '10:00',
    startDate: '2026-07-01',
    endDate: '2026-07-31',
    subject: '语文',
    notes,
  });

describe('schedule attendance window validation', () => {
  it('accepts a configurable number of minutes before class', async () => {
    const dto = createSchedule('');
    dto.attendanceAdvanceMinutes = 45;
    const errors = await validate(dto);
    expect(errors.some((error) => error.property === 'attendanceAdvanceMinutes')).toBe(false);
  });

  it('rejects values outside 0 to 1440 minutes', async () => {
    const dto = Object.assign(new UpdateScheduleDto(), { attendanceAdvanceMinutes: 1441 });
    const errors = await validate(dto);
    expect(errors.some((error) => error.property === 'attendanceAdvanceMinutes')).toBe(true);
  });

  it('accepts only supported schedule statuses when updating', async () => {
    const inactive = Object.assign(new UpdateScheduleDto(), { status: 'inactive' });
    const cancelled = Object.assign(new UpdateScheduleDto(), { status: 'cancelled' });
    const paused = Object.assign(new UpdateScheduleDto(), { status: 'paused' });

    expect((await validate(inactive)).some((error) => error.property === 'status')).toBe(false);
    expect((await validate(cancelled)).some((error) => error.property === 'status')).toBe(false);
    expect((await validate(paused)).some((error) => error.property === 'status')).toBe(true);
  });
});

describe('schedule notes validation', () => {
  it('rejects notes longer than 500 characters when creating', async () => {
    const errors = await validate(createSchedule('a'.repeat(501)));
    expect(errors.some((error) => error.property === 'notes')).toBe(true);
  });

  it('rejects notes longer than 500 characters when updating', async () => {
    const dto = Object.assign(new UpdateScheduleDto(), { notes: 'a'.repeat(501) });
    const errors = await validate(dto);
    expect(errors.some((error) => error.property === 'notes')).toBe(true);
  });
});

it('removes the retired departmentId field from create requests', async () => {
  const dto = Object.assign(new CreateScheduleDto(), {
    classId: 1,
    classroomId: 2,
    weekDay: 1,
    startTime: '09:00',
    endTime: '10:00',
    startDate: '2026-07-01',
    endDate: '2026-07-31',
    subject: '语文',
    departmentId: 99,
  });

  await validate(dto, { whitelist: true });

  expect(dto).not.toHaveProperty('departmentId');
});

describe('schedule boundary validation', () => {
  it.each(['24:00', '09:60', '99:99', '9:00'])('rejects invalid time %s', async (time) => {
    const dto = createSchedule('');
    dto.startTime = time;
    expect((await validate(dto)).some((error) => error.property === 'startTime')).toBe(true);
  });

  it.each(['2026-02-31', '2026-07-13T00:00:00Z', '2026-7-13'])(
    'rejects invalid or non-date-only value %s',
    async (date) => {
      const dto = createSchedule('');
      dto.startDate = date;
      expect((await validate(dto)).some((error) => error.property === 'startDate')).toBe(true);
    },
  );

  it('accepts inclusive attendance-window limits and a leap-day date', async () => {
    const zero = createSchedule('');
    zero.attendanceAdvanceMinutes = 0;
    zero.startDate = '2028-02-29';
    const fullDay = createSchedule('');
    fullDay.attendanceAdvanceMinutes = 1440;
    expect(await validate(zero)).toEqual([]);
    expect(await validate(fullDay)).toEqual([]);
  });
});
