import Fastify from 'fastify';
import { describe, expect, it, vi } from 'vitest';

import { registerTeamRoutes } from '@main/http/teams';
import type { HttpServices } from '@main/http';
import type {
  TeamCreateConfigRequest,
  TeamCreateRequest,
  TeamLaunchRequest,
  TeamLaunchResponse,
  TeamProvisioningProgress,
  TeamRuntimeState,
  TeamSummary,
  TeamViewSnapshot,
} from '@shared/types/team';

describe('HTTP team runtime routes', () => {
  function createServicesMock() {
    const launchTeam =
      vi.fn<
        (
          request: TeamLaunchRequest,
          onProgress: (progress: TeamProvisioningProgress) => void
        ) => Promise<TeamLaunchResponse>
      >();
    const getRuntimeState = vi.fn<(teamName: string) => Promise<TeamRuntimeState>>();
    const getProvisioningStatus = vi.fn<(runId: string) => Promise<TeamProvisioningProgress>>();
    const stopTeam = vi.fn<(teamName: string) => Promise<void>>(() => Promise.resolve());
    const getAliveTeams = vi.fn<() => string[]>();
    const createTeam =
      vi.fn<
        (
          request: TeamCreateRequest,
          onProgress: (progress: TeamProvisioningProgress) => void
        ) => Promise<TeamLaunchResponse>
      >();
    const listTeams = vi.fn<() => Promise<TeamSummary[]>>();
    const getTeamData = vi.fn<(teamName: string) => Promise<TeamViewSnapshot>>();
    const getSavedRequest = vi.fn<(teamName: string) => Promise<TeamCreateRequest | null>>();
    const createTeamConfig = vi.fn<(request: TeamCreateConfigRequest) => Promise<void>>(() =>
      Promise.resolve()
    );
    const teamProvisioningService = {
      createTeam,
      launchTeam,
      getRuntimeState,
      getProvisioningStatus,
      stopTeam,
      getAliveTeams,
    } as Pick<
      NonNullable<HttpServices['teamProvisioningService']>,
      | 'createTeam'
      | 'launchTeam'
      | 'getRuntimeState'
      | 'getProvisioningStatus'
      | 'stopTeam'
      | 'getAliveTeams'
    > as HttpServices['teamProvisioningService'];
    const teamDataService = {
      listTeams,
      getTeamData,
      getSavedRequest,
      createTeamConfig,
    } as Pick<
      NonNullable<HttpServices['teamDataService']>,
      'listTeams' | 'getTeamData' | 'getSavedRequest' | 'createTeamConfig'
    > as HttpServices['teamDataService'];

    const services = {
      projectScanner: {} as HttpServices['projectScanner'],
      sessionParser: {} as HttpServices['sessionParser'],
      subagentResolver: {} as HttpServices['subagentResolver'],
      chunkBuilder: {} as HttpServices['chunkBuilder'],
      dataCache: {} as HttpServices['dataCache'],
      updaterService: {} as HttpServices['updaterService'],
      sshConnectionManager: {} as HttpServices['sshConnectionManager'],
      teamDataService,
      teamProvisioningService,
    } satisfies HttpServices;

    return {
      services,
      launchTeam,
      getRuntimeState,
      getProvisioningStatus,
      stopTeam,
      getAliveTeams,
      createTeam,
      listTeams,
      getTeamData,
      getSavedRequest,
      createTeamConfig,
    };
  }

  async function createApp() {
    const app = Fastify();
    const mocks = createServicesMock();
    registerTeamRoutes(app, mocks.services);
    await app.ready();
    return { app, ...mocks };
  }

  it('lists, gets, and creates draft teams through team data service', async () => {
    const { app, listTeams, getTeamData, createTeamConfig } = await createApp();
    listTeams.mockResolvedValue([
      {
        teamName: 'demo-team',
        displayName: 'Demo Team',
        description: 'Demo',
        memberCount: 1,
        taskCount: 0,
        lastActivity: null,
        pendingCreate: true,
      },
    ]);
    getTeamData.mockResolvedValue({
      teamName: 'demo-team',
      config: null,
      tasks: [],
      messages: [],
      processes: [],
      kanban: null,
    } as unknown as TeamViewSnapshot);

    try {
      const listResponse = await app.inject({
        method: 'GET',
        url: '/api/teams',
      });
      expect(listResponse.statusCode).toBe(200);
      expect(listResponse.json()[0]).toMatchObject({
        teamName: 'demo-team',
        pendingCreate: true,
      });

      const getResponse = await app.inject({
        method: 'GET',
        url: '/api/teams/demo-team',
      });
      expect(getResponse.statusCode).toBe(200);
      expect(getTeamData).toHaveBeenCalledWith('demo-team');

      const createResponse = await app.inject({
        method: 'POST',
        url: '/api/teams',
        payload: {
          teamName: 'new-team',
          displayName: 'New Team',
          members: [{ name: 'builder', role: 'Engineer', providerId: 'codex' }],
          cwd: '/Users/test/project',
          providerId: 'codex',
          model: 'gpt-5.2',
          effort: 'high',
          fastMode: 'on',
          limitContext: true,
        },
      });
      expect(createResponse.statusCode).toBe(201);
      expect(createResponse.json()).toEqual({ teamName: 'new-team' });
      expect(createTeamConfig).toHaveBeenCalledWith({
        teamName: 'new-team',
        displayName: 'New Team',
        members: [
          {
            name: 'builder',
            role: 'Engineer',
            providerId: 'codex',
            providerBackendId: 'codex-native',
          },
        ],
        cwd: '/Users/test/project',
        providerId: 'codex',
        providerBackendId: 'codex-native',
        model: 'gpt-5.2',
        effort: 'high',
        fastMode: 'on',
        limitContext: true,
      });
    } finally {
      await app.close();
    }
  });

  it('launches a team with validated request payload', async () => {
    const { app, launchTeam } = await createApp();
    launchTeam.mockResolvedValue({ runId: 'run-1' });

    try {
      const response = await app.inject({
        method: 'POST',
        url: '/api/teams/demo-team/launch',
        payload: {
          cwd: '/Users/test/project',
          prompt: 'Resume work',
          skipPermissions: false,
          clearContext: true,
        },
      });

      expect(response.statusCode).toBe(200);
      expect(response.json()).toEqual({ runId: 'run-1' });
      expect(launchTeam).toHaveBeenCalledWith(
        {
          teamName: 'demo-team',
          cwd: '/Users/test/project',
          prompt: 'Resume work',
          providerId: 'anthropic',
          skipPermissions: false,
          clearContext: true,
        },
        expect.any(Function)
      );
    } finally {
      await app.close();
    }
  });

  it('validates top-level create effort against the default Anthropic provider over HTTP', async () => {
    const { app, createTeamConfig } = await createApp();

    try {
      const response = await app.inject({
        method: 'POST',
        url: '/api/teams',
        payload: {
          teamName: 'default-anthropic-effort-team',
          members: [{ name: 'builder' }],
          cwd: '/Users/test/project',
          effort: 'max',
        },
      });

      expect(response.statusCode).toBe(201);
      expect(createTeamConfig).toHaveBeenCalledWith({
        teamName: 'default-anthropic-effort-team',
        members: [{ name: 'builder' }],
        cwd: '/Users/test/project',
        effort: 'max',
      });
    } finally {
      await app.close();
    }
  });

  it('validates teammate runtime fields against the inherited top-level provider over HTTP create', async () => {
    const { app, createTeamConfig } = await createApp();

    try {
      const response = await app.inject({
        method: 'POST',
        url: '/api/teams',
        payload: {
          teamName: 'inherited-backend-team',
          members: [{ name: 'builder', providerBackendId: 'codex-native', effort: 'xhigh' }],
          cwd: '/Users/test/project',
          providerId: 'codex',
          providerBackendId: 'codex-native',
        },
      });

      expect(response.statusCode).toBe(201);
      expect(createTeamConfig).toHaveBeenCalledWith({
        teamName: 'inherited-backend-team',
        members: [{ name: 'builder', providerBackendId: 'codex-native', effort: 'xhigh' }],
        cwd: '/Users/test/project',
        providerId: 'codex',
        providerBackendId: 'codex-native',
      });
    } finally {
      await app.close();
    }
  });

  it('drops a stale known backend when launching with a different provider over HTTP', async () => {
    const { app, launchTeam } = await createApp();
    launchTeam.mockResolvedValue({ runId: 'run-2' });

    try {
      const response = await app.inject({
        method: 'POST',
        url: '/api/teams/demo-team/launch',
        payload: {
          cwd: '/Users/test/project',
          providerId: 'anthropic',
          providerBackendId: 'codex-native',
          model: 'sonnet',
          effort: 'low',
        },
      });

      expect(response.statusCode).toBe(200);
      expect(launchTeam).toHaveBeenCalledWith(
        {
          teamName: 'demo-team',
          cwd: '/Users/test/project',
          providerId: 'anthropic',
          model: 'sonnet',
          effort: 'low',
        },
        expect.any(Function)
      );
    } finally {
      await app.close();
    }
  });

  it('still rejects unknown provider backends over HTTP launch', async () => {
    const { app, launchTeam } = await createApp();

    try {
      const response = await app.inject({
        method: 'POST',
        url: '/api/teams/demo-team/launch',
        payload: {
          cwd: '/Users/test/project',
          providerId: 'anthropic',
          providerBackendId: 'unknown-backend',
          model: 'sonnet',
        },
      });

      expect(response.statusCode).toBe(400);
      expect(response.json().error).toContain('providerBackendId must be valid');
      expect(launchTeam).not.toHaveBeenCalled();
    } finally {
      await app.close();
    }
  });

  it('routes draft team launch through createTeam with saved metadata', async () => {
    const { app, createTeam, getSavedRequest, launchTeam } = await createApp();
    getSavedRequest.mockResolvedValue({
      teamName: 'draft-team',
      displayName: 'Draft Team',
      description: 'Saved draft',
      color: '#3366ff',
      cwd: '/Users/test/saved-project',
      prompt: 'Saved prompt',
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.2',
      effort: 'medium',
      fastMode: 'on',
      limitContext: true,
      members: [{ name: 'builder', role: 'Engineer', providerId: 'codex' }],
    });
    createTeam.mockResolvedValue({ runId: 'run-draft' });

    try {
      const response = await app.inject({
        method: 'POST',
        url: '/api/teams/draft-team/launch',
        payload: {
          cwd: '/Users/test/project',
          effort: 'high',
        },
      });

      expect(response.statusCode).toBe(200);
      expect(response.json()).toEqual({ runId: 'run-draft' });
      expect(launchTeam).not.toHaveBeenCalled();
      expect(createTeam).toHaveBeenCalledWith(
        {
          teamName: 'draft-team',
          displayName: 'Draft Team',
          description: 'Saved draft',
          color: '#3366ff',
          members: [{ name: 'builder', role: 'Engineer', providerId: 'codex' }],
          cwd: '/Users/test/project',
          prompt: 'Saved prompt',
          providerId: 'codex',
          providerBackendId: 'codex-native',
          model: 'gpt-5.2',
          effort: 'high',
          fastMode: 'on',
          limitContext: true,
        },
        expect.any(Function)
      );
    } finally {
      await app.close();
    }
  });

  it('drops stale saved draft backend when draft launch switches provider over HTTP', async () => {
    const { app, createTeam, getSavedRequest } = await createApp();
    getSavedRequest.mockResolvedValue({
      teamName: 'draft-team',
      displayName: 'Draft Team',
      cwd: '/Users/test/saved-project',
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.2',
      effort: 'medium',
      limitContext: false,
      members: [{ name: 'builder', role: 'Engineer', providerId: 'codex' }],
    });
    createTeam.mockResolvedValue({ runId: 'run-draft-anthropic' });

    try {
      const response = await app.inject({
        method: 'POST',
        url: '/api/teams/draft-team/launch',
        payload: {
          cwd: '/Users/test/project',
          providerId: 'anthropic',
          model: 'sonnet',
          effort: 'low',
        },
      });

      expect(response.statusCode).toBe(200);
      expect(createTeam).toHaveBeenCalledWith(
        expect.not.objectContaining({ providerBackendId: expect.any(String) }),
        expect.any(Function)
      );
      expect(createTeam).toHaveBeenCalledWith(
        expect.objectContaining({
          teamName: 'draft-team',
          cwd: '/Users/test/project',
          providerId: 'anthropic',
          model: 'sonnet',
          effort: 'low',
        }),
        expect.any(Function)
      );
    } finally {
      await app.close();
    }
  });

  it('does not reuse saved draft model defaults when draft launch switches provider over HTTP', async () => {
    const { app, createTeam, getSavedRequest } = await createApp();
    getSavedRequest.mockResolvedValue({
      teamName: 'draft-team',
      displayName: 'Draft Team',
      cwd: '/Users/test/saved-project',
      providerId: 'codex',
      providerBackendId: 'unknown-stale-backend' as never,
      model: 'gpt-5.2',
      effort: 'medium',
      fastMode: 'on',
      limitContext: true,
      members: [{ name: 'builder', role: 'Engineer', providerId: 'codex' }],
    });
    createTeam.mockResolvedValue({ runId: 'run-draft-anthropic-default' });

    try {
      const response = await app.inject({
        method: 'POST',
        url: '/api/teams/draft-team/launch',
        payload: {
          cwd: '/Users/test/project',
          providerId: 'anthropic',
        },
      });

      expect(response.statusCode).toBe(200);
      const [request] = createTeam.mock.calls.at(-1)!;
      expect(request).toMatchObject({
        teamName: 'draft-team',
        cwd: '/Users/test/project',
        providerId: 'anthropic',
      });
      expect(request.providerBackendId).toBeUndefined();
      expect(request.model).toBeUndefined();
      expect(request.effort).toBeUndefined();
      expect(request.fastMode).toBeUndefined();
      expect(request.limitContext).toBeUndefined();
    } finally {
      await app.close();
    }
  });

  it('clears saved draft model when same-provider draft launch requests default over HTTP', async () => {
    const { app, createTeam, getSavedRequest } = await createApp();
    getSavedRequest.mockResolvedValue({
      teamName: 'draft-team',
      displayName: 'Draft Team',
      cwd: '/Users/test/saved-project',
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.2',
      effort: 'medium',
      limitContext: false,
      members: [{ name: 'builder', role: 'Engineer', providerId: 'codex' }],
    });
    createTeam.mockResolvedValue({ runId: 'run-draft-codex-default' });

    try {
      const response = await app.inject({
        method: 'POST',
        url: '/api/teams/draft-team/launch',
        payload: {
          cwd: '/Users/test/project',
          providerId: 'codex',
          providerBackendId: 'codex-native',
          model: null,
          effort: 'low',
        },
      });

      expect(response.statusCode).toBe(200);
      const [request] = createTeam.mock.calls.at(-1)!;
      expect(request).toMatchObject({
        teamName: 'draft-team',
        cwd: '/Users/test/project',
        providerId: 'codex',
        providerBackendId: 'codex-native',
        effort: 'low',
      });
      expect(request.model).toBeUndefined();
    } finally {
      await app.close();
    }
  });

  it('returns saved metadata for draft team get without requiring config.json', async () => {
    const { app, getSavedRequest, getTeamData } = await createApp();
    getSavedRequest.mockResolvedValue({
      teamName: 'draft-team',
      displayName: 'Draft Team',
      cwd: '/Users/test/project',
      providerId: 'codex',
      providerBackendId: 'codex-native',
      members: [{ name: 'builder', role: 'Engineer', providerId: 'codex' }],
    });

    try {
      const response = await app.inject({
        method: 'GET',
        url: '/api/teams/draft-team',
      });

      expect(response.statusCode).toBe(200);
      expect(response.json()).toEqual({
        teamName: 'draft-team',
        pendingCreate: true,
        savedRequest: {
          teamName: 'draft-team',
          displayName: 'Draft Team',
          cwd: '/Users/test/project',
          providerId: 'codex',
          providerBackendId: 'codex-native',
          members: [{ name: 'builder', role: 'Engineer', providerId: 'codex' }],
        },
      });
      expect(getTeamData).not.toHaveBeenCalled();
    } finally {
      await app.close();
    }
  });

  it('rejects launch requests with non-absolute cwd', async () => {
    const { app, launchTeam } = await createApp();

    try {
      const response = await app.inject({
        method: 'POST',
        url: '/api/teams/demo-team/launch',
        payload: {
          cwd: 'relative/path',
        },
      });

      expect(response.statusCode).toBe(400);
      expect(response.json()).toEqual({ error: 'cwd must be an absolute path' });
      expect(launchTeam).not.toHaveBeenCalled();
    } finally {
      await app.close();
    }
  });

  it('returns runtime state, provisioning status, and stop results', async () => {
    const { app, getRuntimeState, getProvisioningStatus, stopTeam, getAliveTeams } =
      await createApp();
    getRuntimeState
      .mockResolvedValueOnce({
        teamName: 'demo-team',
        isAlive: true,
        runId: 'run-2',
        progress: {
          runId: 'run-2',
          teamName: 'demo-team',
          state: 'ready',
          message: 'Ready',
          startedAt: '2026-03-12T00:00:00.000Z',
          updatedAt: '2026-03-12T00:00:01.000Z',
        },
      })
      .mockResolvedValueOnce({
        teamName: 'demo-team',
        isAlive: false,
        runId: null,
        progress: null,
      })
      .mockResolvedValueOnce({
        teamName: 'demo-team',
        isAlive: true,
        runId: 'run-2',
        progress: {
          runId: 'run-2',
          teamName: 'demo-team',
          state: 'ready',
          message: 'Ready',
          startedAt: '2026-03-12T00:00:00.000Z',
          updatedAt: '2026-03-12T00:00:01.000Z',
        },
      });
    getProvisioningStatus.mockResolvedValue({
      runId: 'run-2',
      teamName: 'demo-team',
      state: 'ready',
      message: 'Ready',
      startedAt: '2026-03-12T00:00:00.000Z',
      updatedAt: '2026-03-12T00:00:01.000Z',
    });
    getAliveTeams.mockReturnValue(['demo-team']);

    try {
      const runtimeResponse = await app.inject({
        method: 'GET',
        url: '/api/teams/demo-team/runtime',
      });
      expect(runtimeResponse.statusCode).toBe(200);
      expect(runtimeResponse.json().isAlive).toBe(true);

      const provisioningResponse = await app.inject({
        method: 'GET',
        url: '/api/teams/provisioning/run-2',
      });
      expect(provisioningResponse.statusCode).toBe(200);
      expect(provisioningResponse.json().runId).toBe('run-2');

      const stopResponse = await app.inject({
        method: 'POST',
        url: '/api/teams/demo-team/stop',
      });
      expect(stopResponse.statusCode).toBe(200);
      expect(stopResponse.json()).toEqual({
        teamName: 'demo-team',
        isAlive: false,
        runId: null,
        progress: null,
      });
      expect(stopTeam).toHaveBeenCalledWith('demo-team');

      const aliveResponse = await app.inject({
        method: 'GET',
        url: '/api/teams/runtime/alive',
      });
      expect(aliveResponse.statusCode).toBe(200);
      expect(aliveResponse.json()).toEqual([
        {
          teamName: 'demo-team',
          isAlive: true,
          runId: 'run-2',
          progress: {
            runId: 'run-2',
            teamName: 'demo-team',
            state: 'ready',
            message: 'Ready',
            startedAt: '2026-03-12T00:00:00.000Z',
            updatedAt: '2026-03-12T00:00:01.000Z',
          },
        },
      ]);
    } finally {
      await app.close();
    }
  });

  it('returns 501 when team runtime routes are registered without a runtime service', async () => {
    const app = Fastify();
    registerTeamRoutes(app, {
      projectScanner: {} as HttpServices['projectScanner'],
      sessionParser: {} as HttpServices['sessionParser'],
      subagentResolver: {} as HttpServices['subagentResolver'],
      chunkBuilder: {} as HttpServices['chunkBuilder'],
      dataCache: {} as HttpServices['dataCache'],
      updaterService: {} as HttpServices['updaterService'],
      sshConnectionManager: {} as HttpServices['sshConnectionManager'],
    } satisfies HttpServices);
    await app.ready();

    try {
      const response = await app.inject({
        method: 'GET',
        url: '/api/teams/runtime/alive',
      });

      expect(response.statusCode).toBe(501);
      expect(response.json()).toEqual({
        error: 'Team runtime control is not available in this mode',
      });
    } finally {
      await app.close();
    }
  });

  it('serves member work sync diagnostics and explicit refresh routes', async () => {
    const app = Fastify();
    const mocks = createServicesMock();
    const queueDiagnostics = {
      queued: 0,
      running: 0,
      enqueued: 2,
      coalesced: 1,
      reconciled: 1,
      dropped: 0,
      failed: 0,
      queuedItems: [],
      runningItems: [],
    };
    const metrics = {
      teamName: 'demo-team',
      generatedAt: '2026-05-05T00:00:00.000Z',
      memberCount: 1,
      stateCounts: {
        caught_up: 1,
        needs_sync: 0,
        still_working: 0,
        blocked: 0,
        inactive: 0,
        unknown: 0,
      },
      actionableItemCount: 0,
      wouldNudgeCount: 0,
      fingerprintChangeCount: 0,
      reportAcceptedCount: 0,
      reportRejectedCount: 0,
      recentEvents: [],
      phase2Readiness: {
        state: 'collecting_shadow_data',
        reasons: ['insufficient_members'],
        thresholds: {
          minObservedMembers: 2,
          minStatusEvents: 10,
          minObservationHours: 1,
          maxWouldNudgesPerMemberHour: 1,
          maxFingerprintChangesPerMemberHour: 1,
          maxReportRejectionRate: 0.1,
        },
        rates: {
          observationHours: 0,
          statusEventCount: 0,
          wouldNudgesPerMemberHour: 0,
          fingerprintChangesPerMemberHour: 0,
          reportRejectionRate: 0,
        },
        diagnostics: [],
      },
    };
    const refreshedStatus = {
      teamName: 'demo-team',
      memberName: 'bob',
      state: 'caught_up',
      agenda: {
        teamName: 'demo-team',
        memberName: 'bob',
        generatedAt: '2026-05-05T00:00:00.000Z',
        fingerprint: 'empty',
        items: [],
        diagnostics: [],
      },
      evaluatedAt: '2026-05-05T00:00:00.000Z',
      diagnostics: [],
    };
    const memberWorkSyncFeature = {
      getStatus: vi.fn(),
      refreshStatus: vi.fn(async () => refreshedStatus),
      getMetrics: vi.fn(async () => metrics),
      report: vi.fn(async () => ({
        accepted: true,
        code: 'accepted',
        message: 'ok',
        status: refreshedStatus,
      })),
      noteTeamChange: vi.fn(),
      enqueueStartupScan: vi.fn(),
      replayPendingReports: vi.fn(),
      dispatchDueNudges: vi.fn(),
      buildRuntimeTurnSettledHookSettings: vi.fn(),
      buildRuntimeTurnSettledEnvironment: vi.fn(),
      drainRuntimeTurnSettledEvents: vi.fn(),
      getQueueDiagnostics: vi.fn(() => queueDiagnostics),
      dispose: vi.fn(),
    };
    registerTeamRoutes(app, {
      ...mocks.services,
      memberWorkSyncFeature: memberWorkSyncFeature as any,
    });
    await app.ready();

    try {
      const diagnosticsResponse = await app.inject({
        method: 'GET',
        url: '/api/teams/demo-team/member-work-sync/diagnostics',
      });
      expect(diagnosticsResponse.statusCode).toBe(200);
      expect(diagnosticsResponse.json()).toMatchObject({
        teamName: 'demo-team',
        queue: queueDiagnostics,
        metrics,
      });

      const refreshResponse = await app.inject({
        method: 'POST',
        url: '/api/teams/demo-team/member-work-sync/bob/refresh',
      });
      expect(refreshResponse.statusCode).toBe(200);
      expect(refreshResponse.json()).toMatchObject(refreshedStatus);
      expect(memberWorkSyncFeature.refreshStatus).toHaveBeenCalledWith({
        teamName: 'demo-team',
        memberName: 'bob',
      });

      const reportResponse = await app.inject({
        method: 'POST',
        url: '/api/teams/demo-team/member-work-sync/report',
        payload: {
          memberName: 'bob',
          state: 'still_working',
          agendaFingerprint: 'agenda:v1:abc',
          reportToken: 'wrs:v1.test.token',
          taskIds: [' task-a ', '', 'task-a'],
        },
      });
      expect(reportResponse.statusCode).toBe(200);
      expect(memberWorkSyncFeature.report).toHaveBeenCalledWith({
        teamName: 'demo-team',
        memberName: 'bob',
        state: 'still_working',
        agendaFingerprint: 'agenda:v1:abc',
        reportToken: 'wrs:v1.test.token',
        taskIds: ['task-a'],
        source: 'mcp',
      });
    } finally {
      await app.close();
    }
  });
});
