import { ValidationPipe } from '@nestjs/common';
import { validate } from 'class-validator';
import { CheckInDto, TransferRoomDto } from './occupancy.dto';

describe('manual occupancy DTO bed requirements', () => {
  it('requires a bed for manual check-in', async () => {
    const dto = Object.assign(new CheckInDto(), {
      studentId: 1,
      roomId: 2,
      checkInDate: '2026-07-13',
    });

    const errors = await validate(dto);

    expect(errors.some((error) => error.property === 'bedId')).toBe(true);
  });

  it('accepts optional deposit collection details for manual check-in', async () => {
    const dto = Object.assign(new CheckInDto(), {
      studentId: 1,
      roomId: 2,
      checkInDate: '2026-07-13',
      bedId: 3,
      collectDeposit: true,
      depositAmount: 500,
    });

    await expect(validate(dto)).resolves.toHaveLength(0);
  });

  it('rejects a non-positive deposit amount', async () => {
    const dto = Object.assign(new CheckInDto(), {
      studentId: 1,
      roomId: 2,
      checkInDate: '2026-07-13',
      bedId: 3,
      collectDeposit: true,
      depositAmount: 0,
    });

    const errors = await validate(dto);

    expect(errors.some((error) => error.property === 'depositAmount')).toBe(true);
  });

  it('strips a manually supplied responsible organization', async () => {
    const pipe = new ValidationPipe({ transform: true, whitelist: true });
    const dto = await pipe.transform(
      {
        studentId: 1,
        roomId: 2,
        checkInDate: '2026-07-13',
        bedId: 3,
        responsibleOrganizationId: 99,
      },
      { type: 'body', metatype: CheckInDto },
    );

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

  it('requires a new bed for a room transfer while keeping the locker optional', async () => {
    const dto = Object.assign(new TransferRoomDto(), {
      newRoomId: 3,
      transferDate: '2026-07-13',
    });

    const errors = await validate(dto);

    expect(errors.some((error) => error.property === 'newBedId')).toBe(true);
    expect(errors.some((error) => error.property === 'newLockerId')).toBe(false);
  });
});

describe('occupancy date boundaries', () => {
  it.each(['2026-02-31', '2026-07-13T00:00:00Z', '2026-7-13'])(
    'rejects invalid or non-date-only check-in date %s',
    async (checkInDate) => {
      const dto = Object.assign(new CheckInDto(), {
        studentId: 1,
        roomId: 2,
        checkInDate,
        bedId: 3,
      });

      expect((await validate(dto)).some((error) => error.property === 'checkInDate')).toBe(true);
    },
  );

  it('accepts a leap-day date', async () => {
    const dto = Object.assign(new CheckInDto(), {
      studentId: 1,
      roomId: 2,
      checkInDate: '2028-02-29',
      bedId: 3,
    });

    await expect(validate(dto)).resolves.toHaveLength(0);
  });
});
