import { BadRequestException } from '@nestjs/common';
import { DepositsService } from './deposits.service';

const createQb = (rows: unknown[] = []) => ({
  innerJoin: jest.fn().mockReturnThis(),
  leftJoin: jest.fn().mockReturnThis(),
  select: jest.fn().mockReturnThis(),
  addSelect: jest.fn().mockReturnThis(),
  where: jest.fn().mockReturnThis(),
  andWhere: jest.fn().mockReturnThis(),
  orderBy: jest.fn().mockReturnThis(),
  addOrderBy: jest.fn().mockReturnThis(),
  getRawMany: jest.fn().mockResolvedValue(rows),
});

function makeService(rows: unknown[] = []) {
  const qb = createQb(rows);
  const repo = {
    findOne: jest.fn(),
    create: jest.fn((value) => value),
    save: jest.fn(async (value) => value),
  };
  const studentRepo = { findOne: jest.fn(async ({ where }: any) => ({ id: where.id })) };
  const occupancyRepo = { createQueryBuilder: jest.fn().mockReturnValue(qb) };
  const service = new DepositsService(repo as never, {} as never, studentRepo as never, occupancyRepo as never);
  return { service, qb, repo, studentRepo };
}

describe('DepositsService room-type deposits', () => {
  it('filters current occupants by room type with capacity fallback', async () => {
    const { service, qb } = makeService([
      {
        studentId: 1,
        studentName: '张三',
        studentNo: 'S1',
        roomId: 8,
        roomNumber: '401',
        building: 'A',
        roomType: null,
        capacity: 4,
        depositAmount: null,
      },
    ]);

    await expect(service.getEligibleStudents('四人间')).resolves.toEqual([
      {
        studentId: 1,
        studentName: '张三',
        studentNo: 'S1',
        roomId: 8,
        roomNumber: '401',
        building: 'A',
        roomType: '四人间',
        capacity: 4,
        depositAmount: 0,
      },
    ]);
    expect(qb.where).toHaveBeenCalledWith('o.status = :activeStatus', { activeStatus: 'active' });
    expect(qb.andWhere).toHaveBeenCalledWith('o.checkOutDate IS NULL');
    expect(qb.andWhere).toHaveBeenCalledWith(
      '(room.roomType = :roomType OR ((room.roomType IS NULL OR room.roomType = :emptyRoomType) AND room.capacity = :fallbackCapacity))',
      { roomType: '四人间', emptyRoomType: '', fallbackCapacity: 4 },
    );
  });

  it('creates or accumulates deposits for a batch of selected students', async () => {
    const { service, repo } = makeService();
    const existing = { id: 1, studentId: 2, amount: 50, status: 'paid' };
    repo.findOne.mockImplementation(async ({ where }: any) => {
      if (where.id) return existing;
      if (where.studentId === 2) return existing;
      return null;
    });

    const result = await service.batchCreate({
      studentIds: [2, 3, 3],
      amount: 100,
      paidDate: '2026-07-17',
      notes: '四人间押金',
    }, 9);

    expect(result.count).toBe(2);
    expect(existing.amount).toBe(150);
    expect(repo.create).toHaveBeenCalledWith(expect.objectContaining({ studentId: 3, amount: 100 }));
    expect(repo.save).toHaveBeenCalledTimes(2);
  });

  it('rejects invalid batch amounts', async () => {
    const { service, repo } = makeService();

    await expect(service.batchCreate({
      studentIds: [1],
      amount: 0.004,
      paidDate: '2026-07-17',
    })).rejects.toBeInstanceOf(BadRequestException);
    expect(repo.save).not.toHaveBeenCalled();
  });
});
