import request from 'supertest';
import { Role } from '../../../src/generated/prisma/enums.js';
import {
  closeE2EContext,
  createE2EContext,
  type E2EContext,
} from '../helpers/e2e-context.helper.js';
import { assertRoleMatrix } from '../helpers/e2e-matrix.helper.js';
import {
  expectErrorEnvelope,
  expectSuccessEnvelope,
  uniqueSeedValue,
} from '../helpers/e2e-http.helper.js';

describe('Organization Controllers (e2e)', () => {
  let ctx: E2EContext;

  beforeAll(async () => {
    ctx = await createE2EContext();
  });

  afterAll(async () => {
    await closeE2EContext(ctx);
  });

  describe('HospitalsController', () => {
    describe('POST /b/organization/hospitals', () => {
      it('成功：SYSTEM_ADMIN 可创建医院', async () => {
        const response = await request(ctx.app.getHttpServer())
          .post('/b/organization/hospitals')
          .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`)
          .send({ name: uniqueSeedValue('组织-医院') });

        expectSuccessEnvelope(response, 201);
        expect(response.body.data.name).toContain('组织-医院');
      });

      it('失败：非系统管理员创建返回 403', async () => {
        const response = await request(ctx.app.getHttpServer())
          .post('/b/organization/hospitals')
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`)
          .send({ name: uniqueSeedValue('组织-医院-失败') });

        expectErrorEnvelope(response, 403, '无权限执行当前操作');
      });

      it('角色矩阵：仅 SYSTEM_ADMIN 可进入业务，其他角色 403，未登录 401', async () => {
        await assertRoleMatrix({
          name: 'POST /b/organization/hospitals role matrix',
          tokens: ctx.tokens,
          expectedStatusByRole: {
            [Role.SYSTEM_ADMIN]: 400,
            [Role.HOSPITAL_ADMIN]: 403,
            [Role.DIRECTOR]: 403,
            [Role.LEADER]: 403,
            [Role.DOCTOR]: 403,
            [Role.ENGINEER]: 403,
          },
          sendAsRole: async (_role, token) =>
            request(ctx.app.getHttpServer())
              .post('/b/organization/hospitals')
              .set('Authorization', `Bearer ${token}`)
              .send({}),
          sendWithoutToken: async () =>
            request(ctx.app.getHttpServer())
              .post('/b/organization/hospitals')
              .send({}),
        });
      });
    });

    describe('GET /b/organization/hospitals', () => {
      it('成功：SYSTEM_ADMIN 可查询医院列表', async () => {
        const response = await request(ctx.app.getHttpServer())
          .get('/b/organization/hospitals')
          .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`);

        expectSuccessEnvelope(response, 200);
        expect(response.body.data).toHaveProperty('list');
      });

      it('失败：未登录返回 401', async () => {
        const response = await request(ctx.app.getHttpServer()).get(
          '/b/organization/hospitals',
        );
        expectErrorEnvelope(response, 401, '缺少 Bearer Token');
      });

      it('角色矩阵：SYSTEM_ADMIN/HOSPITAL_ADMIN/DIRECTOR/LEADER 可访问，其余角色 403，未登录 401', async () => {
        await assertRoleMatrix({
          name: 'GET /b/organization/hospitals role matrix',
          tokens: ctx.tokens,
          expectedStatusByRole: {
            [Role.SYSTEM_ADMIN]: 200,
            [Role.HOSPITAL_ADMIN]: 200,
            [Role.DIRECTOR]: 200,
            [Role.LEADER]: 200,
            [Role.DOCTOR]: 403,
            [Role.ENGINEER]: 403,
          },
          sendAsRole: async (_role, token) =>
            request(ctx.app.getHttpServer())
              .get('/b/organization/hospitals')
              .set('Authorization', `Bearer ${token}`),
          sendWithoutToken: async () =>
            request(ctx.app.getHttpServer()).get('/b/organization/hospitals'),
        });
      });
    });

    describe('GET /b/organization/hospitals/:id', () => {
      it('成功：HOSPITAL_ADMIN 可查询本院详情', async () => {
        const response = await request(ctx.app.getHttpServer())
          .get(`/b/organization/hospitals/${ctx.fixtures.hospitalAId}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`);

        expectSuccessEnvelope(response, 200);
        expect(response.body.data.id).toBe(ctx.fixtures.hospitalAId);
      });

      it('失败：HOSPITAL_ADMIN 查询他院返回 403', async () => {
        const response = await request(ctx.app.getHttpServer())
          .get(`/b/organization/hospitals/${ctx.fixtures.hospitalBId}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`);

        expectErrorEnvelope(response, 403, '院管仅可操作本院组织数据');
      });

      it('角色矩阵：SYSTEM_ADMIN/HOSPITAL_ADMIN/DIRECTOR/LEADER 可访问，其余角色 403，未登录 401', async () => {
        await assertRoleMatrix({
          name: 'GET /b/organization/hospitals/:id role matrix',
          tokens: ctx.tokens,
          expectedStatusByRole: {
            [Role.SYSTEM_ADMIN]: 200,
            [Role.HOSPITAL_ADMIN]: 200,
            [Role.DIRECTOR]: 200,
            [Role.LEADER]: 200,
            [Role.DOCTOR]: 403,
            [Role.ENGINEER]: 403,
          },
          sendAsRole: async (_role, token) =>
            request(ctx.app.getHttpServer())
              .get(`/b/organization/hospitals/${ctx.fixtures.hospitalAId}`)
              .set('Authorization', `Bearer ${token}`),
          sendWithoutToken: async () =>
            request(ctx.app.getHttpServer()).get(
              `/b/organization/hospitals/${ctx.fixtures.hospitalAId}`,
            ),
        });
      });
    });

    describe('PATCH /b/organization/hospitals/:id', () => {
      it('成功：HOSPITAL_ADMIN 可更新本院名称', async () => {
        const originalName = 'Seed Hospital A';
        const nextName = uniqueSeedValue('医院更新');

        const response = await request(ctx.app.getHttpServer())
          .patch(`/b/organization/hospitals/${ctx.fixtures.hospitalAId}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`)
          .send({ name: nextName });

        expectSuccessEnvelope(response, 200);

        const rollbackResponse = await request(ctx.app.getHttpServer())
          .patch(`/b/organization/hospitals/${ctx.fixtures.hospitalAId}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`)
          .send({ name: originalName });
        expectSuccessEnvelope(rollbackResponse, 200);
      });

      it('失败：HOSPITAL_ADMIN 更新他院返回 403', async () => {
        const response = await request(ctx.app.getHttpServer())
          .patch(`/b/organization/hospitals/${ctx.fixtures.hospitalBId}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`)
          .send({ name: uniqueSeedValue('跨院更新失败') });

        expectErrorEnvelope(response, 403, '院管仅可操作本院组织数据');
      });

      it('角色矩阵：SYSTEM_ADMIN/HOSPITAL_ADMIN 可进入业务，其他角色 403，未登录 401', async () => {
        await assertRoleMatrix({
          name: 'PATCH /b/organization/hospitals/:id role matrix',
          tokens: ctx.tokens,
          expectedStatusByRole: {
            [Role.SYSTEM_ADMIN]: 404,
            [Role.HOSPITAL_ADMIN]: 404,
            [Role.DIRECTOR]: 403,
            [Role.LEADER]: 403,
            [Role.DOCTOR]: 403,
            [Role.ENGINEER]: 403,
          },
          sendAsRole: async (_role, token) =>
            request(ctx.app.getHttpServer())
              .patch('/b/organization/hospitals/99999999')
              .set('Authorization', `Bearer ${token}`)
              .send({ name: 'matrix-hospital-patch' }),
          sendWithoutToken: async () =>
            request(ctx.app.getHttpServer())
              .patch('/b/organization/hospitals/99999999')
              .send({ name: 'matrix-hospital-patch' }),
        });
      });
    });

    describe('DELETE /b/organization/hospitals/:id', () => {
      it('成功：SYSTEM_ADMIN 可删除空医院', async () => {
        const createResponse = await request(ctx.app.getHttpServer())
          .post('/b/organization/hospitals')
          .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`)
          .send({ name: uniqueSeedValue('医院待删') });
        expectSuccessEnvelope(createResponse, 201);

        const targetId = createResponse.body.data.id as number;
        const deleteResponse = await request(ctx.app.getHttpServer())
          .delete(`/b/organization/hospitals/${targetId}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`);

        expectSuccessEnvelope(deleteResponse, 200);
        expect(deleteResponse.body.data.id).toBe(targetId);
      });

      it('失败：HOSPITAL_ADMIN 删除医院返回 403', async () => {
        const response = await request(ctx.app.getHttpServer())
          .delete(`/b/organization/hospitals/${ctx.fixtures.hospitalAId}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`);

        expectErrorEnvelope(response, 403, '无权限执行当前操作');
      });

      it('角色矩阵：仅 SYSTEM_ADMIN 可进入业务，其他角色 403，未登录 401', async () => {
        await assertRoleMatrix({
          name: 'DELETE /b/organization/hospitals/:id role matrix',
          tokens: ctx.tokens,
          expectedStatusByRole: {
            [Role.SYSTEM_ADMIN]: 404,
            [Role.HOSPITAL_ADMIN]: 403,
            [Role.DIRECTOR]: 403,
            [Role.LEADER]: 403,
            [Role.DOCTOR]: 403,
            [Role.ENGINEER]: 403,
          },
          sendAsRole: async (_role, token) =>
            request(ctx.app.getHttpServer())
              .delete('/b/organization/hospitals/99999999')
              .set('Authorization', `Bearer ${token}`),
          sendWithoutToken: async () =>
            request(ctx.app.getHttpServer()).delete(
              '/b/organization/hospitals/99999999',
            ),
        });
      });
    });
  });

  describe('DepartmentsController', () => {
    describe('POST /b/organization/departments', () => {
      it('成功：HOSPITAL_ADMIN 可在本院创建科室', async () => {
        const response = await request(ctx.app.getHttpServer())
          .post('/b/organization/departments')
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`)
          .send({
            name: uniqueSeedValue('组织-科室'),
            hospitalId: ctx.fixtures.hospitalAId,
          });

        expectSuccessEnvelope(response, 201);
      });

      it('失败：HOSPITAL_ADMIN 跨院创建返回 403', async () => {
        const response = await request(ctx.app.getHttpServer())
          .post('/b/organization/departments')
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`)
          .send({
            name: uniqueSeedValue('组织-跨院科室失败'),
            hospitalId: ctx.fixtures.hospitalBId,
          });

        expectErrorEnvelope(response, 403, '院管仅可操作本院组织数据');
      });

      it('角色矩阵：SYSTEM_ADMIN/HOSPITAL_ADMIN 可进入业务，其他角色 403，未登录 401', async () => {
        await assertRoleMatrix({
          name: 'POST /b/organization/departments role matrix',
          tokens: ctx.tokens,
          expectedStatusByRole: {
            [Role.SYSTEM_ADMIN]: 400,
            [Role.HOSPITAL_ADMIN]: 400,
            [Role.DIRECTOR]: 403,
            [Role.LEADER]: 403,
            [Role.DOCTOR]: 403,
            [Role.ENGINEER]: 403,
          },
          sendAsRole: async (_role, token) =>
            request(ctx.app.getHttpServer())
              .post('/b/organization/departments')
              .set('Authorization', `Bearer ${token}`)
              .send({}),
          sendWithoutToken: async () =>
            request(ctx.app.getHttpServer())
              .post('/b/organization/departments')
              .send({}),
        });
      });
    });

    describe('GET /b/organization/departments', () => {
      it('成功：HOSPITAL_ADMIN 可查询本院科室列表', async () => {
        const response = await request(ctx.app.getHttpServer())
          .get('/b/organization/departments')
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`);

        expectSuccessEnvelope(response, 200);
        expect(response.body.data).toHaveProperty('list');
      });

      it('失败：未登录返回 401', async () => {
        const response = await request(ctx.app.getHttpServer()).get(
          '/b/organization/departments',
        );
        expectErrorEnvelope(response, 401, '缺少 Bearer Token');
      });

      it('角色矩阵：SYSTEM_ADMIN/HOSPITAL_ADMIN/DIRECTOR/LEADER/DOCTOR 可访问，其余角色 403，未登录 401', async () => {
        await assertRoleMatrix({
          name: 'GET /b/organization/departments role matrix',
          tokens: ctx.tokens,
          expectedStatusByRole: {
            [Role.SYSTEM_ADMIN]: 200,
            [Role.HOSPITAL_ADMIN]: 200,
            [Role.DIRECTOR]: 200,
            [Role.LEADER]: 200,
            [Role.DOCTOR]: 200,
            [Role.ENGINEER]: 403,
          },
          sendAsRole: async (_role, token) =>
            request(ctx.app.getHttpServer())
              .get('/b/organization/departments')
              .set('Authorization', `Bearer ${token}`),
          sendWithoutToken: async () =>
            request(ctx.app.getHttpServer()).get('/b/organization/departments'),
        });
      });
    });

    describe('GET /b/organization/departments/:id', () => {
      it('成功：SYSTEM_ADMIN 可查询科室详情', async () => {
        const response = await request(ctx.app.getHttpServer())
          .get(`/b/organization/departments/${ctx.fixtures.departmentA1Id}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`);

        expectSuccessEnvelope(response, 200);
        expect(response.body.data.id).toBe(ctx.fixtures.departmentA1Id);
      });

      it('失败：HOSPITAL_ADMIN 查询他院科室返回 403', async () => {
        const response = await request(ctx.app.getHttpServer())
          .get(`/b/organization/departments/${ctx.fixtures.departmentB1Id}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`);

        expectErrorEnvelope(response, 403, '院管仅可操作本院组织数据');
      });

      it('角色矩阵：SYSTEM_ADMIN/HOSPITAL_ADMIN/DIRECTOR/LEADER/DOCTOR 可访问，其余角色 403，未登录 401', async () => {
        await assertRoleMatrix({
          name: 'GET /b/organization/departments/:id role matrix',
          tokens: ctx.tokens,
          expectedStatusByRole: {
            [Role.SYSTEM_ADMIN]: 200,
            [Role.HOSPITAL_ADMIN]: 200,
            [Role.DIRECTOR]: 200,
            [Role.LEADER]: 200,
            [Role.DOCTOR]: 200,
            [Role.ENGINEER]: 403,
          },
          sendAsRole: async (_role, token) =>
            request(ctx.app.getHttpServer())
              .get(`/b/organization/departments/${ctx.fixtures.departmentA1Id}`)
              .set('Authorization', `Bearer ${token}`),
          sendWithoutToken: async () =>
            request(ctx.app.getHttpServer()).get(
              `/b/organization/departments/${ctx.fixtures.departmentA1Id}`,
            ),
        });
      });
    });

    describe('PATCH /b/organization/departments/:id', () => {
      it('成功：HOSPITAL_ADMIN 可更新本院科室', async () => {
        const originalName = 'Cardiology-A2';
        const nextName = uniqueSeedValue('科室更新');

        const response = await request(ctx.app.getHttpServer())
          .patch(`/b/organization/departments/${ctx.fixtures.departmentA2Id}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`)
          .send({ name: nextName });

        expectSuccessEnvelope(response, 200);

        const rollbackResponse = await request(ctx.app.getHttpServer())
          .patch(`/b/organization/departments/${ctx.fixtures.departmentA2Id}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`)
          .send({ name: originalName });
        expectSuccessEnvelope(rollbackResponse, 200);
      });

      it('失败：HOSPITAL_ADMIN 更新他院科室返回 403', async () => {
        const response = await request(ctx.app.getHttpServer())
          .patch(`/b/organization/departments/${ctx.fixtures.departmentB1Id}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`)
          .send({ name: uniqueSeedValue('跨院科室更新失败') });

        expectErrorEnvelope(response, 403, '院管仅可操作本院组织数据');
      });

      it('角色矩阵：SYSTEM_ADMIN/HOSPITAL_ADMIN 可进入业务，其余角色 403，未登录 401', async () => {
        await assertRoleMatrix({
          name: 'PATCH /b/organization/departments/:id role matrix',
          tokens: ctx.tokens,
          expectedStatusByRole: {
            [Role.SYSTEM_ADMIN]: 404,
            [Role.HOSPITAL_ADMIN]: 404,
            [Role.DIRECTOR]: 403,
            [Role.LEADER]: 403,
            [Role.DOCTOR]: 403,
            [Role.ENGINEER]: 403,
          },
          sendAsRole: async (_role, token) =>
            request(ctx.app.getHttpServer())
              .patch('/b/organization/departments/99999999')
              .set('Authorization', `Bearer ${token}`)
              .send({ name: 'matrix-department-patch' }),
          sendWithoutToken: async () =>
            request(ctx.app.getHttpServer())
              .patch('/b/organization/departments/99999999')
              .send({ name: 'matrix-department-patch' }),
        });
      });
    });

    describe('DELETE /b/organization/departments/:id', () => {
      it('成功：SYSTEM_ADMIN 可删除无关联科室', async () => {
        const createResponse = await request(ctx.app.getHttpServer())
          .post('/b/organization/departments')
          .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`)
          .send({
            name: uniqueSeedValue('科室待删'),
            hospitalId: ctx.fixtures.hospitalAId,
          });
        expectSuccessEnvelope(createResponse, 201);

        const targetId = createResponse.body.data.id as number;
        const deleteResponse = await request(ctx.app.getHttpServer())
          .delete(`/b/organization/departments/${targetId}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`);

        expectSuccessEnvelope(deleteResponse, 200);
      });

      it('失败：存在关联数据删除返回 409', async () => {
        const response = await request(ctx.app.getHttpServer())
          .delete(`/b/organization/departments/${ctx.fixtures.departmentA1Id}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`);

        expectErrorEnvelope(response, 409, '存在关联数据，无法删除');
      });

      it('角色矩阵：SYSTEM_ADMIN/HOSPITAL_ADMIN 可进入业务，其他角色 403，未登录 401', async () => {
        await assertRoleMatrix({
          name: 'DELETE /b/organization/departments/:id role matrix',
          tokens: ctx.tokens,
          expectedStatusByRole: {
            [Role.SYSTEM_ADMIN]: 404,
            [Role.HOSPITAL_ADMIN]: 404,
            [Role.DIRECTOR]: 403,
            [Role.LEADER]: 403,
            [Role.DOCTOR]: 403,
            [Role.ENGINEER]: 403,
          },
          sendAsRole: async (_role, token) =>
            request(ctx.app.getHttpServer())
              .delete('/b/organization/departments/99999999')
              .set('Authorization', `Bearer ${token}`),
          sendWithoutToken: async () =>
            request(ctx.app.getHttpServer()).delete(
              '/b/organization/departments/99999999',
            ),
        });
      });
    });
  });

  describe('GroupsController', () => {
    describe('POST /b/organization/groups', () => {
      it('成功：HOSPITAL_ADMIN 可创建小组', async () => {
        const response = await request(ctx.app.getHttpServer())
          .post('/b/organization/groups')
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`)
          .send({
            name: uniqueSeedValue('组织-小组'),
            departmentId: ctx.fixtures.departmentA1Id,
          });

        expectSuccessEnvelope(response, 201);
      });

      it('失败：HOSPITAL_ADMIN 跨院创建小组返回 403', async () => {
        const response = await request(ctx.app.getHttpServer())
          .post('/b/organization/groups')
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`)
          .send({
            name: uniqueSeedValue('组织-跨院小组失败'),
            departmentId: ctx.fixtures.departmentB1Id,
          });

        expectErrorEnvelope(response, 403, '院管仅可操作本院组织数据');
      });

      it('角色矩阵：SYSTEM_ADMIN/HOSPITAL_ADMIN 可进入业务，其余角色 403，未登录 401', async () => {
        await assertRoleMatrix({
          name: 'POST /b/organization/groups role matrix',
          tokens: ctx.tokens,
          expectedStatusByRole: {
            [Role.SYSTEM_ADMIN]: 400,
            [Role.HOSPITAL_ADMIN]: 400,
            [Role.DIRECTOR]: 403,
            [Role.LEADER]: 403,
            [Role.DOCTOR]: 403,
            [Role.ENGINEER]: 403,
          },
          sendAsRole: async (_role, token) =>
            request(ctx.app.getHttpServer())
              .post('/b/organization/groups')
              .set('Authorization', `Bearer ${token}`)
              .send({}),
          sendWithoutToken: async () =>
            request(ctx.app.getHttpServer())
              .post('/b/organization/groups')
              .send({}),
        });
      });
    });

    describe('GET /b/organization/groups', () => {
      it('成功：SYSTEM_ADMIN 可查询小组列表', async () => {
        const response = await request(ctx.app.getHttpServer())
          .get('/b/organization/groups')
          .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`);

        expectSuccessEnvelope(response, 200);
        expect(response.body.data).toHaveProperty('list');
      });

      it('失败：未登录返回 401', async () => {
        const response = await request(ctx.app.getHttpServer()).get(
          '/b/organization/groups',
        );
        expectErrorEnvelope(response, 401, '缺少 Bearer Token');
      });

      it('角色矩阵：SYSTEM_ADMIN/HOSPITAL_ADMIN/DIRECTOR/LEADER/DOCTOR 可访问，其余角色 403，未登录 401', async () => {
        await assertRoleMatrix({
          name: 'GET /b/organization/groups role matrix',
          tokens: ctx.tokens,
          expectedStatusByRole: {
            [Role.SYSTEM_ADMIN]: 200,
            [Role.HOSPITAL_ADMIN]: 200,
            [Role.DIRECTOR]: 200,
            [Role.LEADER]: 200,
            [Role.DOCTOR]: 200,
            [Role.ENGINEER]: 403,
          },
          sendAsRole: async (_role, token) =>
            request(ctx.app.getHttpServer())
              .get('/b/organization/groups')
              .set('Authorization', `Bearer ${token}`),
          sendWithoutToken: async () =>
            request(ctx.app.getHttpServer()).get('/b/organization/groups'),
        });
      });
    });

    describe('GET /b/organization/groups/:id', () => {
      it('成功：HOSPITAL_ADMIN 可查询本院小组详情', async () => {
        const response = await request(ctx.app.getHttpServer())
          .get(`/b/organization/groups/${ctx.fixtures.groupA1Id}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`);

        expectSuccessEnvelope(response, 200);
        expect(response.body.data.id).toBe(ctx.fixtures.groupA1Id);
      });

      it('失败：HOSPITAL_ADMIN 查询他院小组返回 403', async () => {
        const response = await request(ctx.app.getHttpServer())
          .get(`/b/organization/groups/${ctx.fixtures.groupB1Id}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`);

        expectErrorEnvelope(response, 403, '院管仅可操作本院组织数据');
      });

      it('角色矩阵：SYSTEM_ADMIN/HOSPITAL_ADMIN/DIRECTOR/LEADER/DOCTOR 可访问，其余角色 403，未登录 401', async () => {
        await assertRoleMatrix({
          name: 'GET /b/organization/groups/:id role matrix',
          tokens: ctx.tokens,
          expectedStatusByRole: {
            [Role.SYSTEM_ADMIN]: 200,
            [Role.HOSPITAL_ADMIN]: 200,
            [Role.DIRECTOR]: 200,
            [Role.LEADER]: 200,
            [Role.DOCTOR]: 200,
            [Role.ENGINEER]: 403,
          },
          sendAsRole: async (_role, token) =>
            request(ctx.app.getHttpServer())
              .get(`/b/organization/groups/${ctx.fixtures.groupA1Id}`)
              .set('Authorization', `Bearer ${token}`),
          sendWithoutToken: async () =>
            request(ctx.app.getHttpServer()).get(
              `/b/organization/groups/${ctx.fixtures.groupA1Id}`,
            ),
        });
      });
    });

    describe('PATCH /b/organization/groups/:id', () => {
      it('成功：HOSPITAL_ADMIN 可更新本院小组', async () => {
        const originalName = 'Shift-A2';
        const nextName = uniqueSeedValue('小组更新');

        const response = await request(ctx.app.getHttpServer())
          .patch(`/b/organization/groups/${ctx.fixtures.groupA2Id}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`)
          .send({ name: nextName });

        expectSuccessEnvelope(response, 200);

        const rollbackResponse = await request(ctx.app.getHttpServer())
          .patch(`/b/organization/groups/${ctx.fixtures.groupA2Id}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`)
          .send({ name: originalName });
        expectSuccessEnvelope(rollbackResponse, 200);
      });

      it('失败：HOSPITAL_ADMIN 更新他院小组返回 403', async () => {
        const response = await request(ctx.app.getHttpServer())
          .patch(`/b/organization/groups/${ctx.fixtures.groupB1Id}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`)
          .send({ name: uniqueSeedValue('跨院小组更新失败') });

        expectErrorEnvelope(response, 403, '院管仅可操作本院组织数据');
      });

      it('角色矩阵：SYSTEM_ADMIN/HOSPITAL_ADMIN 可进入业务，其余角色 403，未登录 401', async () => {
        await assertRoleMatrix({
          name: 'PATCH /b/organization/groups/:id role matrix',
          tokens: ctx.tokens,
          expectedStatusByRole: {
            [Role.SYSTEM_ADMIN]: 404,
            [Role.HOSPITAL_ADMIN]: 404,
            [Role.DIRECTOR]: 403,
            [Role.LEADER]: 403,
            [Role.DOCTOR]: 403,
            [Role.ENGINEER]: 403,
          },
          sendAsRole: async (_role, token) =>
            request(ctx.app.getHttpServer())
              .patch('/b/organization/groups/99999999')
              .set('Authorization', `Bearer ${token}`)
              .send({ name: 'matrix-group-patch' }),
          sendWithoutToken: async () =>
            request(ctx.app.getHttpServer())
              .patch('/b/organization/groups/99999999')
              .send({ name: 'matrix-group-patch' }),
        });
      });
    });

    describe('DELETE /b/organization/groups/:id', () => {
      it('成功：SYSTEM_ADMIN 可删除无关联小组', async () => {
        const createResponse = await request(ctx.app.getHttpServer())
          .post('/b/organization/groups')
          .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`)
          .send({
            name: uniqueSeedValue('小组待删'),
            departmentId: ctx.fixtures.departmentA1Id,
          });
        expectSuccessEnvelope(createResponse, 201);

        const targetId = createResponse.body.data.id as number;
        const deleteResponse = await request(ctx.app.getHttpServer())
          .delete(`/b/organization/groups/${targetId}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`);

        expectSuccessEnvelope(deleteResponse, 200);
      });

      it('失败：HOSPITAL_ADMIN 删除他院小组返回 403', async () => {
        const response = await request(ctx.app.getHttpServer())
          .delete(`/b/organization/groups/${ctx.fixtures.groupB1Id}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`);

        expectErrorEnvelope(response, 403, '院管仅可操作本院组织数据');
      });

      it('失败：删除有成员的小组返回 409', async () => {
        const response = await request(ctx.app.getHttpServer())
          .delete(`/b/organization/groups/${ctx.fixtures.groupA1Id}`)
          .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`);

        expectErrorEnvelope(response, 409, '小组下仍有成员，无法删除');
      });

      it('角色矩阵：SYSTEM_ADMIN/HOSPITAL_ADMIN 可进入业务，其余角色 403，未登录 401', async () => {
        await assertRoleMatrix({
          name: 'DELETE /b/organization/groups/:id role matrix',
          tokens: ctx.tokens,
          expectedStatusByRole: {
            [Role.SYSTEM_ADMIN]: 404,
            [Role.HOSPITAL_ADMIN]: 404,
            [Role.DIRECTOR]: 403,
            [Role.LEADER]: 403,
            [Role.DOCTOR]: 403,
            [Role.ENGINEER]: 403,
          },
          sendAsRole: async (_role, token) =>
            request(ctx.app.getHttpServer())
              .delete('/b/organization/groups/99999999')
              .set('Authorization', `Bearer ${token}`),
          sendWithoutToken: async () =>
            request(ctx.app.getHttpServer()).delete(
              '/b/organization/groups/99999999',
            ),
        });
      });
    });
  });
});
