import 'reflect-metadata';
import { PIPES_METADATA } from '@nestjs/common/constants';
import { PERMISSION_KEY } from '../auth/decorators/permission.decorator';
import { ExpensesController } from '../expenses/expenses.controller';
import { OccupanciesController } from '../occupancies/occupancies.controller';
import { RoomsController } from '../rooms/rooms.controller';
import { StudentsController } from '../students/students.controller';
import { BatchIdsDto } from './batch-ids.dto';

describe('batch restore controllers', () => {
  const cases = [
    [StudentsController, 'batchRestore', ['student:edit']],
    [RoomsController, 'batchRestore', ['room:edit']],
    [ExpensesController, 'batchRestoreRoomExpenses', ['expense:edit']],
    [ExpensesController, 'batchRestorePersonalExpenses', ['expense:edit']],
    [OccupanciesController, 'batchRestore', ['occupancy:delete']],
  ] as const;

  it.each(cases)('%p.%s has permission and method-level validation', (controller, method, permission) => {
    const handler = controller.prototype[method] as (...args: never[]) => unknown;
    expect(Reflect.getMetadata(PERMISSION_KEY, handler)).toEqual(permission);
    expect(Reflect.getMetadata(PIPES_METADATA, handler)).toHaveLength(1);
  });

  it.each(cases)('%p.%s rejects invalid and non-whitelisted request bodies', async (controller, method) => {
    const handler = controller.prototype[method] as (...args: never[]) => unknown;
    const [pipe] = Reflect.getMetadata(PIPES_METADATA, handler);
    const metadata = { type: 'body' as const, metatype: BatchIdsDto, data: undefined };
    await expect(pipe.transform({ ids: [] }, metadata)).rejects.toBeDefined();
    await expect(pipe.transform({ ids: [0] }, metadata)).rejects.toBeDefined();
    await expect(pipe.transform({ ids: [1], unexpected: true }, metadata)).rejects.toBeDefined();
  });

  it('writes the requested audit action and ids for every successful restore endpoint', async () => {
    const log = jest.fn().mockResolvedValue(undefined);
    const req = { user: { id: 7, username: 'admin' }, ip: '127.0.0.1', headers: {} };
    const services = {
      students: { batchRestore: jest.fn().mockResolvedValue({ restored: 1, skipped: 0 }) },
      rooms: { batchRestore: jest.fn().mockResolvedValue({ restored: 1, skipped: 0 }) },
      expenses: {
        batchRestoreRoomExpenses: jest.fn().mockResolvedValue({ restored: 1, skipped: 0 }),
        batchRestorePersonalExpenses: jest.fn().mockResolvedValue({ restored: 1, skipped: 0 }),
      },
      occupancies: { batchRestore: jest.fn().mockResolvedValue({ restored: 1, skipped: 0 }) },
    };
    const students = new StudentsController(services.students as never, { log } as never, {} as never, {} as never);
    const rooms = new RoomsController(services.rooms as never, { log } as never, {} as never);
    const expenses = new ExpensesController(services.expenses as never, { log } as never);
    const occupancies = new OccupanciesController(services.occupancies as never, { log } as never, {} as never, {} as never);

    await students.batchRestore({ ids: [1, 2] }, req);
    await rooms.batchRestore({ ids: [1, 2] }, req);
    await expenses.batchRestoreRoomExpenses({ ids: [1, 2] }, req);
    await expenses.batchRestorePersonalExpenses({ ids: [1, 2] }, req);
    await occupancies.batchRestore({ ids: [1, 2] }, req);

    expect(log.mock.calls.map(([entry]) => [entry.action, entry.detail])).toEqual([
      ['批量恢复学生', 'IDs: 1,2'],
      ['批量恢复宿舍', 'IDs: 1,2'],
      ['批量恢复宿舍费用', 'IDs: 1,2'],
      ['批量恢复个人费用', 'IDs: 1,2'],
      ['批量恢复入住记录', 'IDs: 1,2'],
    ]);
  });
});
