import sharp from 'sharp';
import request from 'supertest';
import { Role, TaskStatus } 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,
  uniquePhone,
  uniqueSeedValue,
} from '../helpers/e2e-http.helper.js';
import { loginByMiniApp } from '../helpers/e2e-miniapp-auth.helper.js';

function uniqueIdCard() {
  const suffix = `${Date.now()}${Math.floor(Math.random() * 1000)}`
    .replace(/\D/g, '')
    .slice(-4);
  return `11010119990101${suffix.padStart(4, '0')}`;
}

describe('BTasksController (e2e)', () => {
  let ctx: E2EContext;
  let samplePngBuffer: Buffer;
  let doctorBToken = '';
  let doctorA2Token = '';

  beforeAll(async () => {
    ctx = await createE2EContext();
    samplePngBuffer = await sharp({
      create: {
        width: 32,
        height: 32,
        channels: 3,
        background: { r: 42, g: 78, b: 126 },
      },
    })
      .png()
      .toBuffer();
    doctorBToken = await loginByUser(
      ctx.fixtures.users.doctorBId,
      Role.DOCTOR,
      ctx.fixtures.hospitalBId,
    );
    doctorA2Token = await loginByUser(
      ctx.fixtures.users.doctorA2Id,
      Role.DOCTOR,
      ctx.fixtures.hospitalAId,
    );
  });

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

  async function publishPendingTask(
    deviceId: number,
    targetPressure: string,
    actorToken = ctx.tokens[Role.DOCTOR],
  ) {
    const response = await request(ctx.app.getHttpServer())
      .post('/b/tasks/publish')
      .set('Authorization', `Bearer ${actorToken}`)
      .send({
        items: [
          {
            deviceId,
            targetPressure,
          },
        ],
      });

    expectSuccessEnvelope(response, 201);
    return response.body.data as {
      id: number;
      status: TaskStatus;
      engineerId: number | null;
      hospitalId: number;
    };
  }

  async function acceptPendingTask(
    taskId: number,
    actorToken = ctx.tokens[Role.ENGINEER],
  ) {
    const response = await request(ctx.app.getHttpServer())
      .post('/b/tasks/accept')
      .set('Authorization', `Bearer ${actorToken}`)
      .send({ taskId });

    expectSuccessEnvelope(response, 201);
    return response.body.data as {
      id: number;
      status: TaskStatus;
      engineerId: number;
    };
  }

  async function loginByUser(userId: number, role: Role, hospitalId: number) {
    const user = await ctx.prisma.user.findUnique({
      where: { id: userId },
      select: { phone: true, openId: true },
    });
    expect(user?.phone).toBeTruthy();
    expect(user?.openId).toBeTruthy();

    return loginByMiniApp(ctx.app.getHttpServer(), {
      phone: user!.phone,
      openId: user!.openId!,
      role,
      hospitalId,
      userId,
    });
  }

  async function createAdjustableDevices(options?: {
    actorToken?: string;
    doctorId?: number;
    count?: number;
    patientName?: string;
    projectName?: string;
  }) {
    const {
      actorToken = ctx.tokens[Role.DOCTOR],
      doctorId = ctx.fixtures.users.doctorAId,
      count = 1,
      patientName = '调压测试患者',
      projectName = '调压测试项目',
    } = options ?? {};

    const response = await request(ctx.app.getHttpServer())
      .post('/b/patients')
      .set('Authorization', `Bearer ${actorToken}`)
      .send({
        name: `${patientName}-${uniqueSeedValue('name')}`,
        inpatientNo: uniqueSeedValue('zyh'),
        projectName,
        phone: uniquePhone(),
        idCard: uniqueIdCard(),
        doctorId,
        initialSurgery: {
          surgeryDate: '2026-03-20T08:00:00.000Z',
          surgeryName: '调压测试手术',
          preOpPressure: 18,
          primaryDisease: '交通性脑积水',
          hydrocephalusTypes: ['交通性'],
          devices: Array.from({ length: count }, (_, index) => ({
            implantCatalogId: ctx.fixtures.catalogs.adjustableValveId,
            shuntMode: 'VPS',
            proximalPunctureAreas: [index % 2 === 0 ? '额角' : '枕角'],
            valvePlacementSites: [index % 2 === 0 ? '耳后' : '胸前'],
            distalShuntDirection: '腹腔',
            initialPressure: index % 2 === 0 ? '1' : '1.5',
            implantNotes: uniqueSeedValue(`task-device-${index + 1}`),
          })),
        },
      });

    expectSuccessEnvelope(response, 201);
    return response.body.data.devices as Array<{ id: number }>;
  }

  async function uploadEngineerProof(actorToken = ctx.tokens[Role.ENGINEER]) {
    const response = await request(ctx.app.getHttpServer())
      .post('/b/uploads')
      .set('Authorization', `Bearer ${actorToken}`)
      .attach('file', samplePngBuffer, {
        filename: 'task-proof.png',
        contentType: 'image/png',
      });

    expectSuccessEnvelope(response, 201);
    return response.body.data as {
      id: number;
      type: 'IMAGE' | 'VIDEO';
      url: string;
      originalName?: string;
      fileName?: string;
    };
  }

  function buildCompletionPayload(
    taskId: number,
    asset: Awaited<ReturnType<typeof uploadEngineerProof>>,
  ) {
    return {
      taskId,
      completionMaterials: [
        {
          assetId: asset.id,
          type: asset.type,
          url: asset.url,
          name: asset.originalName || asset.fileName || '调压完成照片',
        },
      ],
    };
  }

  describe('GET /b/tasks/engineers', () => {
    it('成功：DOCTOR 可查看本院可选工程师', async () => {
      const response = await request(ctx.app.getHttpServer())
        .get('/b/tasks/engineers')
        .set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`);

      expectSuccessEnvelope(response, 200);
      expect(Array.isArray(response.body.data)).toBe(true);
      expect(response.body.data).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            id: ctx.fixtures.users.engineerAId,
          }),
        ]),
      );
    });

    it('成功：SYSTEM_ADMIN 可按医院筛选工程师', async () => {
      const response = await request(ctx.app.getHttpServer())
        .get('/b/tasks/engineers')
        .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`)
        .query({ hospitalId: ctx.fixtures.hospitalBId });

      expectSuccessEnvelope(response, 200);
      expect(response.body.data).toEqual([
        expect.objectContaining({
          id: ctx.fixtures.users.engineerBId,
          hospitalId: ctx.fixtures.hospitalBId,
        }),
      ]);
    });

    it('失败：hospitalId 非法返回 400', async () => {
      const response = await request(ctx.app.getHttpServer())
        .get('/b/tasks/engineers')
        .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`)
        .query({ hospitalId: 0 });

      expectErrorEnvelope(response, 400, 'hospitalId 必须大于 0');
    });
  });

  describe('GET /b/tasks', () => {
    it('成功：SYSTEM_ADMIN 可查看跨医院调压记录', async () => {
      const [deviceA] = await createAdjustableDevices();
      const [deviceB] = await createAdjustableDevices({
        actorToken: doctorBToken,
        doctorId: ctx.fixtures.users.doctorBId,
        patientName: 'B院调压患者',
      });

      await publishPendingTask(
        deviceA.id,
        '1.5',
        ctx.tokens[Role.SYSTEM_ADMIN],
      );
      await publishPendingTask(
        deviceB.id,
        '1.5',
        ctx.tokens[Role.SYSTEM_ADMIN],
      );

      const response = await request(ctx.app.getHttpServer())
        .get('/b/tasks')
        .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`)
        .query({ page: 1, pageSize: 20 });

      expectSuccessEnvelope(response, 200);
      expect(Array.isArray(response.body.data.list)).toBe(true);
      expect(response.body.data.total).toBeGreaterThan(0);
      expect(
        response.body.data.list.every(
          (item: {
            creator?: { id?: number; name?: string };
            engineer?: { id?: number; name?: string } | null;
            patient?: { phone?: string };
            patientPhone?: string;
          }) =>
            Number.isInteger(item.creator?.id) &&
            Boolean(item.creator?.name) &&
            Boolean(item.patient?.phone) &&
            Boolean(item.patientPhone),
        ),
      ).toBe(true);

      const hospitalIds = Array.from(
        new Set(
          response.body.data.list
            .map((item: { hospital?: { id?: number } }) => item.hospital?.id)
            .filter(Boolean),
        ),
      );
      expect(hospitalIds).toEqual(
        expect.arrayContaining([
          ctx.fixtures.hospitalAId,
          ctx.fixtures.hospitalBId,
        ]),
      );
    });

    it('成功：DOCTOR 仅可查看本人患者调压记录', async () => {
      const keywordPrefix = uniqueSeedValue('scope-doctor');
      const [selfDevice] = await createAdjustableDevices({
        actorToken: ctx.tokens[Role.DOCTOR],
        doctorId: ctx.fixtures.users.doctorAId,
        patientName: `${keywordPrefix}-self`,
      });
      const [peerDevice] = await createAdjustableDevices({
        actorToken: doctorA2Token,
        doctorId: ctx.fixtures.users.doctorA2Id,
        patientName: `${keywordPrefix}-peer`,
      });

      await publishPendingTask(
        selfDevice.id,
        '1.5',
        ctx.tokens[Role.SYSTEM_ADMIN],
      );
      await publishPendingTask(
        peerDevice.id,
        '1.5',
        ctx.tokens[Role.SYSTEM_ADMIN],
      );

      const response = await request(ctx.app.getHttpServer())
        .get('/b/tasks')
        .set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`)
        .query({ page: 1, pageSize: 50, keyword: keywordPrefix });

      expectSuccessEnvelope(response, 200);
      expect(Array.isArray(response.body.data.list)).toBe(true);
      expect(response.body.data.total).toBeGreaterThan(0);
      expect(
        response.body.data.list.every(
          (item: {
            hospital?: { id?: number };
            patient?: { name?: string };
          }) =>
            item.hospital?.id === ctx.fixtures.hospitalAId &&
            item.patient?.name?.includes(`${keywordPrefix}-self`),
        ),
      ).toBe(true);
      expect(
        response.body.data.list.some((item: { patient?: { name?: string } }) =>
          item.patient?.name?.includes(`${keywordPrefix}-peer`),
        ),
      ).toBe(false);
    });

    it('失败：hospitalId 非法返回 400', async () => {
      const response = await request(ctx.app.getHttpServer())
        .get('/b/tasks')
        .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`)
        .query({ hospitalId: 0 });

      expectErrorEnvelope(response, 400, 'hospitalId 必须大于 0');
    });
  });

  describe('POST /b/tasks/publish', () => {
    it('成功：DOCTOR 发布任务后进入待接收状态', async () => {
      const [device] = await createAdjustableDevices();
      const response = await request(ctx.app.getHttpServer())
        .post('/b/tasks/publish')
        .set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`)
        .send({
          items: [
            {
              deviceId: device.id,
              targetPressure: '1.5',
            },
          ],
        });

      expectSuccessEnvelope(response, 201);
      expect(response.body.data.status).toBe(TaskStatus.PENDING);
      expect(response.body.data.engineerId).toBeNull();
    });

    it('成功：SYSTEM_ADMIN 可按设备自动归院发布任务', async () => {
      const [device] = await createAdjustableDevices();
      const task = await publishPendingTask(
        device.id,
        '1.5',
        ctx.tokens[Role.SYSTEM_ADMIN],
      );

      expect(task.status).toBe(TaskStatus.PENDING);
      expect(task.hospitalId).toBe(ctx.fixtures.hospitalAId);
    });

    it('失败：可调压设备使用非法挡位返回 400', async () => {
      const [device] = await createAdjustableDevices();
      const response = await request(ctx.app.getHttpServer())
        .post('/b/tasks/publish')
        .set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`)
        .send({
          items: [
            {
              deviceId: device.id,
              targetPressure: '2',
            },
          ],
        });

      expectErrorEnvelope(response, 400, '压力值不在该植入物配置的挡位范围内');
    });

    it('失败：发布跨院设备任务返回 404', async () => {
      const response = await request(ctx.app.getHttpServer())
        .post('/b/tasks/publish')
        .set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`)
        .send({
          items: [
            {
              deviceId: ctx.fixtures.devices.deviceB1Id,
              targetPressure: '1.5',
            },
          ],
        });

      expectErrorEnvelope(response, 404, '存在设备不在当前医院或设备不存在');
    });

    it('失败：已有待处理任务的设备不可重复发布', async () => {
      const [device] = await createAdjustableDevices();
      await publishPendingTask(device.id, '1.5');

      const duplicateResponse = await request(ctx.app.getHttpServer())
        .post('/b/tasks/publish')
        .set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`)
        .send({
          items: [
            {
              deviceId: device.id,
              targetPressure: '1',
            },
          ],
        });

      expectErrorEnvelope(duplicateResponse, 409, '该设备已有待处理调压任务');
    });

    it('成功：同一患者另一台设备无任务时仍可发布', async () => {
      const createdDevices = await createAdjustableDevices({ count: 2 });
      expect(createdDevices).toHaveLength(2);

      await publishPendingTask(createdDevices[0].id, '1.5');

      const response = await request(ctx.app.getHttpServer())
        .post('/b/tasks/publish')
        .set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`)
        .send({
          items: [
            {
              deviceId: createdDevices[1].id,
              targetPressure: '1',
            },
          ],
        });

      expectSuccessEnvelope(response, 201);
      expect(response.body.data.status).toBe(TaskStatus.PENDING);
    });

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

  describe('POST /b/tasks/accept', () => {
    it('成功：ENGINEER 可接收本院待处理任务', async () => {
      const [device] = await createAdjustableDevices();
      const task = await publishPendingTask(device.id, '1.5');

      const acceptedTask = await acceptPendingTask(task.id);
      expect(acceptedTask.status).toBe(TaskStatus.ACCEPTED);
      expect(acceptedTask.engineerId).toBe(ctx.fixtures.users.engineerAId);
    });

    it('失败：跨院工程师不能接收任务', async () => {
      const [device] = await createAdjustableDevices();
      const task = await publishPendingTask(device.id, '1.5');
      const engineerB = await ctx.prisma.user.findUnique({
        where: { id: ctx.fixtures.users.engineerBId },
        select: { phone: true },
      });
      expect(engineerB?.phone).toBeTruthy();

      const loginResponse = await request(ctx.app.getHttpServer())
        .post('/auth/miniapp/b/phone-login')
        .send({
          loginCode: `mock-login:${encodeURIComponent('seed-engineer-b-openid')}`,
          phoneCode: `mock-phone:${engineerB?.phone}`,
        });
      expectSuccessEnvelope(loginResponse, 201);

      const response = await request(ctx.app.getHttpServer())
        .post('/b/tasks/accept')
        .set('Authorization', `Bearer ${loginResponse.body.data.accessToken}`)
        .send({ taskId: task.id });

      expectErrorEnvelope(response, 404, '任务不存在或不属于当前医院');
    });

    it('状态机失败：已被接收的任务不可再次接收', async () => {
      const [device] = await createAdjustableDevices();
      const task = await publishPendingTask(device.id, '1.5');
      await acceptPendingTask(task.id);

      const response = await request(ctx.app.getHttpServer())
        .post('/b/tasks/accept')
        .set('Authorization', `Bearer ${ctx.tokens[Role.ENGINEER]}`)
        .send({ taskId: task.id });

      expectErrorEnvelope(response, 409, '仅待接收任务可执行接收');
    });

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

  describe('POST /b/tasks/complete', () => {
    it('成功：ENGINEER 可完成自己已接收的任务并同步设备压力', async () => {
      const targetPressure = '1.5';
      const [device] = await createAdjustableDevices();
      const task = await publishPendingTask(device.id, targetPressure);
      await acceptPendingTask(task.id);
      const proof = await uploadEngineerProof();

      const completeResponse = await request(ctx.app.getHttpServer())
        .post('/b/tasks/complete')
        .set('Authorization', `Bearer ${ctx.tokens[Role.ENGINEER]}`)
        .send(buildCompletionPayload(task.id, proof));

      expectSuccessEnvelope(completeResponse, 201);
      expect(completeResponse.body.data.status).toBe(TaskStatus.COMPLETED);
      expect(completeResponse.body.data.completionMaterials).toEqual([
        expect.objectContaining({
          assetId: proof.id,
          type: 'IMAGE',
          url: proof.url,
        }),
      ]);

      const updatedDevice = await ctx.prisma.device.findUnique({
        where: { id: device.id },
        select: { currentPressure: true },
      });
      expect(updatedDevice?.currentPressure).toBe(targetPressure);
    });

    it('失败：完成不存在任务返回 404', async () => {
      const proof = await uploadEngineerProof();
      const response = await request(ctx.app.getHttpServer())
        .post('/b/tasks/complete')
        .set('Authorization', `Bearer ${ctx.tokens[Role.ENGINEER]}`)
        .send(buildCompletionPayload(99999999, proof));

      expectErrorEnvelope(response, 404, '任务不存在或不属于当前医院');
    });

    it('失败：未上传完成凭证返回 400', async () => {
      const [device] = await createAdjustableDevices();
      const task = await publishPendingTask(device.id, '1.5');
      await acceptPendingTask(task.id);

      const response = await request(ctx.app.getHttpServer())
        .post('/b/tasks/complete')
        .set('Authorization', `Bearer ${ctx.tokens[Role.ENGINEER]}`)
        .send({
          taskId: task.id,
          completionMaterials: [],
        });

      expectErrorEnvelope(response, 400, 'completionMaterials 至少上传 1 项');
    });

    it('状态机失败：重复完成返回 409', async () => {
      const [device] = await createAdjustableDevices();
      const task = await publishPendingTask(device.id, '1');
      await acceptPendingTask(task.id);
      const proof = await uploadEngineerProof();

      const firstComplete = await request(ctx.app.getHttpServer())
        .post('/b/tasks/complete')
        .set('Authorization', `Bearer ${ctx.tokens[Role.ENGINEER]}`)
        .send(buildCompletionPayload(task.id, proof));
      expectSuccessEnvelope(firstComplete, 201);

      const secondComplete = await request(ctx.app.getHttpServer())
        .post('/b/tasks/complete')
        .set('Authorization', `Bearer ${ctx.tokens[Role.ENGINEER]}`)
        .send(buildCompletionPayload(task.id, proof));

      expectErrorEnvelope(secondComplete, 409, '仅已接收任务可执行完成');
    });

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

  describe('POST /b/tasks/cancel', () => {
    it('成功：DOCTOR 可取消自己创建的待接收任务', async () => {
      const [device] = await createAdjustableDevices();
      const task = await publishPendingTask(device.id, '1.5');

      const response = await request(ctx.app.getHttpServer())
        .post('/b/tasks/cancel')
        .set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`)
        .send({ taskId: task.id });

      expectSuccessEnvelope(response, 201);
      expect(response.body.data.status).toBe(TaskStatus.CANCELLED);
    });

    it('成功：ENGINEER 可取消接收并退回待接收状态', async () => {
      const [device] = await createAdjustableDevices();
      const task = await publishPendingTask(device.id, '1.5');
      await acceptPendingTask(task.id);

      const response = await request(ctx.app.getHttpServer())
        .post('/b/tasks/cancel')
        .set('Authorization', `Bearer ${ctx.tokens[Role.ENGINEER]}`)
        .send({ taskId: task.id, reason: '患者临时取消' });

      expectSuccessEnvelope(response, 201);
      expect(response.body.data.status).toBe(TaskStatus.PENDING);
      expect(response.body.data.engineerId).toBeNull();

      const secondAcceptResponse = await request(ctx.app.getHttpServer())
        .post('/b/tasks/accept')
        .set('Authorization', `Bearer ${ctx.tokens[Role.ENGINEER]}`)
        .send({ taskId: task.id });

      expectSuccessEnvelope(secondAcceptResponse, 201);
      expect(secondAcceptResponse.body.data.status).toBe(TaskStatus.ACCEPTED);
    });

    it('失败：取消不存在任务返回 404', async () => {
      const response = await request(ctx.app.getHttpServer())
        .post('/b/tasks/cancel')
        .set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`)
        .send({ taskId: 99999999 });

      expectErrorEnvelope(response, 404, '任务不存在或不属于当前医院');
    });

    it('状态机失败：已完成任务不可取消返回 409', async () => {
      const [device] = await createAdjustableDevices();
      const task = await publishPendingTask(device.id, '1.5');
      await acceptPendingTask(task.id);
      const proof = await uploadEngineerProof();

      const completeResponse = await request(ctx.app.getHttpServer())
        .post('/b/tasks/complete')
        .set('Authorization', `Bearer ${ctx.tokens[Role.ENGINEER]}`)
        .send(buildCompletionPayload(task.id, proof));
      expectSuccessEnvelope(completeResponse, 201);

      const cancelResponse = await request(ctx.app.getHttpServer())
        .post('/b/tasks/cancel')
        .set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`)
        .send({ taskId: task.id });

      expectErrorEnvelope(cancelResponse, 409, '仅待接收/已接收任务可取消');
    });

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