import { http } from 'msw' import { jsonResponse, noContent, paginate, sse } from '../helpers' import { seedMedia } from '../../fixtures/seed_media' import { seedAnnotations } from '../../fixtures/seed_annotations' import { seedUserSettings } from '../../fixtures/seed_user_settings' // Default `/api/annotations/*` handlers — media list, annotation CRUD, dataset, // status SSE. The annotation status SSE returns a small canned event sequence // so dataset / annotations tests don't have to register their own stream just // to mount a component. export const annotationsHandlers = [ http.get('/api/annotations/media', ({ request }) => { const url = new URL(request.url) const page = Number(url.searchParams.get('page') ?? '1') const pageSize = Number(url.searchParams.get('pageSize') ?? String(seedMedia.length)) return jsonResponse(paginate(seedMedia, page, pageSize)) }), http.get('/api/annotations/media/:id', ({ params }) => { const m = seedMedia.find((x) => x.id === params.id) if (!m) return new Response(null, { status: 404 }) return jsonResponse(m) }), http.get('/api/annotations/media/:id/annotations', ({ params }) => jsonResponse(seedAnnotations.filter((a) => a.mediaId === params.id)), ), // Production routes use the doubly-prefixed canary `/api/annotations/annotations/*` // — gateway prefix `/api/annotations/` + service base `/annotations/`. AZ-460 AC-1 // pins this path; the static check would catch a single-prefix regression. http.get('/api/annotations/annotations', ({ request }) => { const url = new URL(request.url) const mediaId = url.searchParams.get('mediaId') const items = mediaId ? seedAnnotations.filter((a) => a.mediaId === mediaId) : seedAnnotations const page = Number(url.searchParams.get('page') ?? '1') const pageSize = Number(url.searchParams.get('pageSize') ?? String(items.length)) return jsonResponse(paginate(items, page, pageSize)) }), http.post('/api/annotations/annotations', async ({ request }) => { const body = (await request.json()) as Record return jsonResponse({ id: 'ann-new', createdDate: new Date().toISOString(), ...body }, { status: 201 }) }), http.patch('/api/annotations/annotations/:id/status', async ({ request, params }) => { const body = (await request.json()) as { status?: number } return jsonResponse({ id: params.id, status: body.status ?? 10 }) }), http.delete('/api/annotations/annotations/:id', () => noContent()), // Single-prefix variants kept for backward compatibility with existing tests // that may rely on them. Production uses doubly-prefixed (above). http.get('/api/annotations', () => jsonResponse(seedAnnotations)), http.post('/api/annotations', async ({ request }) => { const body = (await request.json()) as Record return jsonResponse({ id: 'ann-new', createdDate: new Date().toISOString(), ...body }, { status: 201 }) }), http.patch('/api/annotations/:id/status', async ({ request, params }) => { const body = (await request.json()) as { status?: number } return jsonResponse({ id: params.id, status: body.status ?? 10 }) }), http.delete('/api/annotations/:id', () => noContent()), http.get('/api/annotations/dataset', () => jsonResponse( seedAnnotations.map((a) => ({ annotationId: a.id, imageName: `image-${a.mediaId}.jpg`, thumbnailPath: `/thumbs/${a.mediaId}.jpg`, status: a.status, createdDate: a.createdDate, createdEmail: 'op_alice@test.local', flightName: 'Flight 1', source: a.source, isSeed: false, isSplit: a.isSplit, })), ), ), http.post('/api/annotations/dataset/bulk-status', async ({ request }) => { const body = (await request.json()) as { ids?: string[]; status?: number } return jsonResponse({ updated: body.ids?.length ?? 0, status: body.status ?? 30 }) }), http.get('/api/annotations/dataset/distribution', () => jsonResponse([ { classNum: 0, label: 'class-0', color: '#ff0000', count: 12 }, { classNum: 1, label: 'class-1', color: '#00ff00', count: 7 }, ]), ), http.get('/api/annotations/status', () => sse([ { event: 'status', data: { annotationId: seedAnnotations[0]?.id ?? 'ann-1', status: 20 }, id: '1' }, { event: 'status', data: { annotationId: seedAnnotations[0]?.id ?? 'ann-1', status: 30 }, id: '2' }, ]), ), http.get('/api/annotations/users/:userId/settings', ({ params }) => { const s = seedUserSettings.find((x) => x.userId === params.userId) if (!s) return new Response(null, { status: 404 }) return jsonResponse(s) }), http.put('/api/annotations/users/:userId/settings', async ({ request, params }) => { const body = (await request.json()) as Record return jsonResponse({ id: 'user-settings-1', userId: params.userId, ...body }) }), // System / directory settings — used by `` (production paths). http.get('/api/annotations/settings/system', () => jsonResponse({ id: 'sys-settings-1', name: 'Test System', militaryUnit: null, defaultCameraWidth: 1920, defaultCameraFoV: 60, }), ), http.put('/api/annotations/settings/system', async ({ request }) => jsonResponse(await request.json()), ), http.get('/api/annotations/settings/directories', () => jsonResponse({ id: 'dirs-1', videosDir: '/srv/videos', imagesDir: '/srv/images', labelsDir: '/srv/labels', resultsDir: '/srv/results', thumbnailsDir: '/srv/thumbs', gpsSatDir: '/srv/gps-sat', gpsRouteDir: '/srv/gps-route', }), ), http.put('/api/annotations/settings/directories', async ({ request }) => jsonResponse(await request.json()), ), // Used by AdminPage when listing detection classes for the editor. http.get('/api/annotations/classes', () => jsonResponse([ { id: 1, name: 'class-a', shortName: 'a', color: '#ff0000', maxSizeM: 7 }, { id: 2, name: 'class-b', shortName: 'b', color: '#00ff00', maxSizeM: 5 }, ]), ), ]