import { NotFoundException } from '@nestjs/common';
import { UpdateStudentsTool } from './update-students.tool';

function createTool(overrides: Record<string, unknown> = {}) {
  const studentsService = {
    update: jest.fn(async (id: number, dto: Record<string, unknown>) => ({
      id,
      name: dto.name ?? '学生',
    })),
    ...(overrides.studentsService ?? {}),
  };
  const tool = new UpdateStudentsTool(studentsService as never);
  return { tool, studentsService };
}

const validInput = {
  updates: [
    { id: 201, name: '於嘉丽' },
    { id: 172, name: '徐玚' },
  ],
};

describe('UpdateStudentsTool', () => {
  it('exposes student:edit permission and student skill', () => {
    const { tool } = createTool();
    expect(tool.name).toBe('update_students');
    expect(tool.skillKey).toBe('student');
    expect(tool.requiredPermission).toBe('student:edit');
  });

  it('rejects forbidden, unknown, and empty input', () => {
    const { tool } = createTool();
    expect(tool.validate({ userId: 1 }).ok).toBe(false);
    expect(tool.validate({ updates: [], admin: true }).ok).toBe(false);
    expect(tool.validate({}).ok).toBe(false);
    expect(tool.validate({ updates: [] }).ok).toBe(false);
    expect(tool.validate({ updates: [{ id: 201 }] }).ok).toBe(false);
  });

  it('rejects invalid ids, duplicate ids, invalid fields, and oversized batches', () => {
    const { tool } = createTool();
    expect(tool.validate({ updates: [{ id: 0, name: 'A' }] }).ok).toBe(false);
    expect(tool.validate({ updates: [{ id: 'x', name: 'A' }] }).ok).toBe(false);
    expect(
      tool.validate({
        updates: [
          { id: 201, name: 'A' },
          { id: 201, name: 'B' },
        ],
      }).ok,
    ).toBe(false);
    expect(tool.validate({ updates: [{ id: 201, status: 'archived' }] }).ok).toBe(false);
    expect(tool.validate({ updates: [{ id: 201, name: 'A'.repeat(51) }] }).ok).toBe(false);
    expect(
      tool.validate({
        updates: Array.from({ length: 13 }, (_, index) => ({ id: index + 1, name: 'A' })),
      }).ok,
    ).toBe(false);
  });

  it('accepts all supported editable fields', () => {
    const { tool } = createTool();
    const result = tool.validate({
      updates: [
        {
          id: 201,
          name: '於嘉丽',
          studentNo: 'S201',
          phone: '13800138000',
          idNumber: 'ID201',
          gender: '女',
          ethnicity: '汉族',
          emergencyContact: '家长',
          emergencyPhone: '13900139000',
          organizationId: 2,
          supervisor: '王老师',
          status: 'active',
        },
      ],
    });
    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.updates[0]).toMatchObject({
        id: 201,
        name: '於嘉丽',
        organizationId: 2,
        status: 'active',
      });
    }
  });

  it('updates every student and reports a safe summary', async () => {
    const { tool, studentsService } = createTool();
    const result = await tool.execute(validInput, {} as never);
    expect(studentsService.update).toHaveBeenCalledTimes(2);
    expect(studentsService.update).toHaveBeenCalledWith(201, { name: '於嘉丽' });
    expect(result).toEqual({
      message: '成功更新 2 名学生，失败 0 条',
      updated: [
        { id: 201, name: '於嘉丽' },
        { id: 172, name: '徐玚' },
      ],
      failed: [],
    });
    expect(JSON.stringify(result)).not.toContain('13800138000');
  });

  it('continues when one student cannot be updated', async () => {
    const { tool } = createTool({
      studentsService: {
        update: jest
          .fn()
          .mockRejectedValueOnce(new NotFoundException('not found'))
          .mockResolvedValueOnce({ id: 172, name: '徐玚' }),
      },
    });
    const result = await tool.execute(validInput, {} as never);
    expect(result).toEqual({
      message: '成功更新 1 名学生，失败 1 条',
      updated: [{ id: 172, name: '徐玚' }],
      failed: [{ id: 201, error: '学生不存在' }],
    });
  });
});
