// AZ-486 / F7 — Single source of truth for every `/api/<service>/<path>` URL
// the UI talks to today. Closes architecture baseline finding F7.
//
// Every UI callsite of `api.*`, `createSSE`, and raw image/video `src` URLs
// pointing at an API resource MUST go through one of these builders. The
// STC-ARCH-02 static gate (scripts/check-arch-imports.mjs `--mode=api-literals`,
// wired into scripts/run-tests.sh) enforces it.
//
// **Wire-contract invariant**: the strings produced here are character-identical
// to the literals that lived in the source before this refactor and that MSW
// handlers + e2e stubs intercept. Any change to a builder's output is a wire-
// contract change and MUST be coordinated with the backend + the MSW handler
// surface + e2e stubs in the same commit. The accompanying test file
// (`endpoints.test.ts`) pins every URL string and is the contract documentation.
//
// **Why function form (not constants)**: per user direction at task-creation
// time; allows parameter interpolation without callsite re-introducing template
// literals and keeps tree-shaking per-builder under Vite's production rollup.

export const endpoints = {
  admin: {
    authRefresh: () => '/api/admin/auth/refresh',
    authLogin: () => '/api/admin/auth/login',
    authLogout: () => '/api/admin/auth/logout',
    users: () => '/api/admin/users',
    // AZ-510 — chained from POST authRefresh() during AuthProvider bootstrap
    // (the POST refresh response is `{ token }` only; the user shape comes
    // from this GET). Keeps `01_api-transport` as the single source of truth
    // for `/api/admin/...` literals (STC-ARCH-02).
    usersMe: () => '/api/admin/users/me',
    user: (id: string) => `/api/admin/users/${id}`,
    classes: () => '/api/admin/classes',
    // DetectionClass.id is `number` in the type system; widened to accept
    // string for forward-compat if the backend switches the column to UUID.
    class: (id: string | number) => `/api/admin/classes/${id}`,
    // v2 admin page — mocked via MSW until the backend lands the endpoints.
    aiSettings: () => '/api/admin/ai-settings',
    gpsSettings: () => '/api/admin/gps-settings',
    gpsPing: () => '/api/admin/gps-settings/ping',
    gpsReconnect: () => '/api/admin/gps-settings/reconnect',
  },
  annotations: {
    classes: () => '/api/annotations/classes',
    settingsUser: () => '/api/annotations/settings/user',
    settingsSystem: () => '/api/annotations/settings/system',
    settingsDirectories: () => '/api/annotations/settings/directories',
    annotations: () => '/api/annotations/annotations',
    // page-size is currently always 1000 at every callsite; expose it as an
    // optional param so future tuning is a single-file change.
    annotationsByMedia: (mediaId: string, pageSize: number = 1000) =>
      `/api/annotations/annotations?mediaId=${mediaId}&pageSize=${pageSize}`,
    annotationImage: (annotationId: string) =>
      `/api/annotations/annotations/${annotationId}/image`,
    annotationThumbnail: (annotationId: string) =>
      `/api/annotations/annotations/${annotationId}/thumbnail`,
    annotationEvents: () => '/api/annotations/annotations/events',
    // Callers pre-build a URLSearchParams.toString() and pass it through; the
    // builder owns the path + `?` only so the wire-contract stays identical.
    media: (queryString: string) => `/api/annotations/media?${queryString}`,
    mediaFile: (mediaId: string) => `/api/annotations/media/${mediaId}/file`,
    mediaItem: (mediaId: string) => `/api/annotations/media/${mediaId}`,
    mediaBatch: () => '/api/annotations/media/batch',
    dataset: (queryString: string) => `/api/annotations/dataset?${queryString}`,
    datasetItem: (annotationId: string) =>
      `/api/annotations/dataset/${annotationId}`,
    datasetBulkStatus: () => '/api/annotations/dataset/bulk-status',
    datasetClassDistribution: () =>
      '/api/annotations/dataset/class-distribution',
  },
  flights: {
    // GET (with `?pageSize=...`) lists flights; POST (no query) creates one.
    // The query string is owned by the caller (URLSearchParams.toString()) so
    // the wire-contract stays identical to today.
    collection: (queryString?: string) =>
      queryString ? `/api/flights?${queryString}` : '/api/flights',
    aircrafts: () => '/api/flights/aircrafts',
    aircraft: (id: string) => `/api/flights/aircrafts/${id}`,
    flight: (id: string) => `/api/flights/${id}`,
    flightWaypoints: (id: string) => `/api/flights/${id}/waypoints`,
    flightWaypoint: (flightId: string, waypointId: string) =>
      `/api/flights/${flightId}/waypoints/${waypointId}`,
    flightLiveGps: (id: string) => `/api/flights/${id}/live-gps`,
  },
  detect: {
    // Trigger detection for a media item. Single-segment service path.
    media: (mediaId: string) => `/api/detect/${mediaId}`,
  },
} as const
