import type {
  AdminAnalyticsResponse,
  AdminAiImageProviderTestResponse,
  AdminAiProviderTestResponse,
  AdminImageUploadResponse,
  AdminMediaBatchDeleteResponse,
  AdminMediaDownloadResponse,
  AdminMediaDeleteResponse,
  AdminMediaListResponse,
  AdminMediaMetadataResponse,
  AdminMediaReplaceResponse,
  AdminMediaUploadResponse,
  AdminPostCoverImageRequest,
  AdminPostCoverImageResponse,
  AdminPostLocalizeImagesResponse,
  AdminDashboardResponse,
  AdminPostMetadataResponse,
  AdminPostPolishResponse,
  AdminReviewPolishRequest,
  AdminReviewPolishResponse,
  AdminR2ConnectivityResponse,
  AdminSessionResponse,
  AdminSiteSettingsResponse,
  AuditLogRecord,
  CategoryRecord,
  CommentListQuery,
  CommentBlacklistRecord,
  CommentPersonaAnalysisLogRecord,
  CommentPersonaAnalysisResponse,
  CommentRecord,
  CreatePostPayload,
  CreateReviewPayload,
  FriendLinkListQuery,
  FriendLinkPayload,
  FriendLinkRecord,
  MarkdownDeleteResponse,
  MarkdownDocumentResponse,
  MarkdownImportResponse,
  MediaAssetMetadataPayload,
  MediaDownloadPayload,
  NotificationDeliveryRecord,
  PostPageResponse,
  PostListQuery,
  PostRevisionDetail,
  PostRevisionRecord,
  PostRecord,
  ReviewRecord,
  RestoreRevisionResponse,
  SiteSettingsPayload,
  SiteBackupDocument,
  SiteBackupImportPayload,
  SiteBackupImportResponse,
  SubscriptionDigestResponse,
  SubscriptionListResponse,
  SubscriptionPayload,
  SubscriptionRecord,
  SubscriptionUpdatePayload,
  WorkerJobListResponse,
  WorkerJobRecord,
  WorkerOverview,
  WorkerTaskActionResponse,
  TagRecord,
  TaxonomyPayload,
  UpdateCommentPayload,
  UpdatePostPayload,
  UpdateReviewPayload,
} from '@/lib/types'
import { getRuntimeAdminBaseUrl, normalizeAdminBaseUrl } from '@/lib/runtime-config'

const envApiBase = normalizeAdminBaseUrl(import.meta.env.VITE_API_BASE)
const PROD_DEFAULT_API_PORT = '5150'
const DEV_DEFAULT_API_HOST = '127.0.0.1'

function getApiBase() {
  const runtimeApiBase = getRuntimeAdminBaseUrl('apiBaseUrl')
  if (runtimeApiBase) {
    return runtimeApiBase
  }

  if (envApiBase) {
    return envApiBase
  }

  if (import.meta.env.DEV) {
    if (typeof window !== 'undefined') {
      const { protocol, hostname } = window.location
      return `${protocol}//${hostname}:${PROD_DEFAULT_API_PORT}`
    }

    return `http://${DEV_DEFAULT_API_HOST}:${PROD_DEFAULT_API_PORT}`
  }

  const { protocol, hostname } = window.location
  return `${protocol}//${hostname}:${PROD_DEFAULT_API_PORT}`
}

const API_BASE = getApiBase()

export class ApiError extends Error {
  status: number

  constructor(message: string, status: number) {
    super(message)
    this.name = 'ApiError'
    this.status = status
  }
}

async function readErrorMessage(response: Response) {
  const text = await response.text().catch(() => '')

  if (!text) {
    return `请求失败，状态码 ${response.status}。`
  }

  try {
    const parsed = JSON.parse(text) as { description?: string; error?: string; message?: string }
    return parsed.description || parsed.error || parsed.message || text
  } catch {
    return text
  }
}

function appendQueryParams(path: string, params?: Record<string, unknown>) {
  if (!params) {
    return path
  }

  const searchParams = new URLSearchParams()

  Object.entries(params).forEach(([key, value]) => {
    if (value === undefined || value === null || value === '') {
      return
    }

    if (typeof value === 'boolean') {
      searchParams.set(key, String(value))
      return
    }

    searchParams.set(key, String(value))
  })

  const queryString = searchParams.toString()
  return queryString ? `${path}?${queryString}` : path
}

async function request<T>(path: string, init?: RequestInit): Promise<T> {
  const headers = new Headers(init?.headers)

  if (init?.body && !(init.body instanceof FormData) && !headers.has('Content-Type')) {
    headers.set('Content-Type', 'application/json')
  }

  const response = await fetch(`${API_BASE}${path}`, {
    ...init,
    credentials: 'include',
    headers,
  })

  if (!response.ok) {
    throw new ApiError(await readErrorMessage(response), response.status)
  }

  if (response.status === 204) {
    return undefined as T
  }

  const contentType = response.headers.get('content-type') || ''

  if (contentType.includes('application/json')) {
    return (await response.json()) as T
  }

  return (await response.text()) as T
}

export const adminApi = {
  sessionStatus: () => request<AdminSessionResponse>('/api/admin/session'),
  login: (payload: { username: string; password: string }) =>
    request<AdminSessionResponse>('/api/admin/session/login', {
      method: 'POST',
      body: JSON.stringify(payload),
    }),
  logout: () =>
    request<AdminSessionResponse>('/api/admin/session', {
      method: 'DELETE',
    }),
  listAuditLogs: (query?: { action?: string; targetType?: string; limit?: number }) =>
    request<AuditLogRecord[]>(
      appendQueryParams('/api/admin/audit-logs', {
        action: query?.action,
        target_type: query?.targetType,
        limit: query?.limit,
      }),
    ),
  listPostRevisions: (query?: { slug?: string; limit?: number }) =>
    request<PostRevisionRecord[]>(
      appendQueryParams('/api/admin/post-revisions', {
        slug: query?.slug,
        limit: query?.limit,
      }),
    ),
  getPostRevision: (id: number) => request<PostRevisionDetail>(`/api/admin/post-revisions/${id}`),
  restorePostRevision: (id: number, mode: 'full' | 'markdown' | 'metadata' = 'full') =>
    request<RestoreRevisionResponse>(`/api/admin/post-revisions/${id}/restore`, {
      method: 'POST',
      body: JSON.stringify({ mode }),
    }),
  listSubscriptions: async () =>
    (await request<SubscriptionListResponse>('/api/admin/subscriptions')).subscriptions,
  createSubscription: (payload: SubscriptionPayload) =>
    request<SubscriptionRecord>('/api/admin/subscriptions', {
      method: 'POST',
      body: JSON.stringify({
        channelType: payload.channelType,
        target: payload.target,
        displayName: payload.displayName,
        status: payload.status,
        filters: payload.filters,
        metadata: payload.metadata,
        secret: payload.secret,
        notes: payload.notes,
      }),
    }),
  updateSubscription: (id: number, payload: SubscriptionUpdatePayload) =>
    request<SubscriptionRecord>(`/api/admin/subscriptions/${id}`, {
      method: 'PATCH',
      body: JSON.stringify({
        channelType: payload.channelType,
        target: payload.target,
        displayName: payload.displayName,
        status: payload.status,
        filters: payload.filters,
        metadata: payload.metadata,
        secret: payload.secret,
        notes: payload.notes,
      }),
    }),
  deleteSubscription: (id: number) =>
    request<void>(`/api/admin/subscriptions/${id}`, {
      method: 'DELETE',
    }),
  testSubscription: (id: number) =>
    request<{ queued: boolean; id: number; delivery_id: number; job_id?: number | null }>(`/api/admin/subscriptions/${id}/test`, {
      method: 'POST',
    }),
  listSubscriptionDeliveries: async (limit = 80) =>
    (await request<{ deliveries: NotificationDeliveryRecord[] }>(
      appendQueryParams('/api/admin/subscriptions/deliveries', { limit }),
    )).deliveries,
  sendSubscriptionDigest: (period: 'weekly' | 'monthly') =>
    request<SubscriptionDigestResponse>('/api/admin/subscriptions/digest', {
      method: 'POST',
      body: JSON.stringify({ period }),
    }),
  getWorkersOverview: () => request<WorkerOverview>('/api/admin/workers/overview'),
  listWorkerJobs: (query?: {
    status?: string
    jobKind?: string
    workerName?: string
    search?: string
    limit?: number
  }) =>
    request<WorkerJobListResponse>(
      appendQueryParams('/api/admin/workers/jobs', {
        status: query?.status,
        job_kind: query?.jobKind,
        worker_name: query?.workerName,
        search: query?.search,
        limit: query?.limit,
      }),
    ),
  getWorkerJob: (id: number) => request<WorkerJobRecord>(`/api/admin/workers/jobs/${id}`),
  cancelWorkerJob: (id: number) =>
    request<WorkerJobRecord>(`/api/admin/workers/jobs/${id}/cancel`, {
      method: 'POST',
    }),
  retryWorkerJob: (id: number) =>
    request<WorkerTaskActionResponse>(`/api/admin/workers/jobs/${id}/retry`, {
      method: 'POST',
    }),
  runRetryDeliveriesWorker: (limit?: number) =>
    request<WorkerTaskActionResponse>('/api/admin/workers/tasks/retry-deliveries', {
      method: 'POST',
      body: JSON.stringify({ limit }),
    }),
  runDigestWorker: (period: 'weekly' | 'monthly') =>
    request<WorkerTaskActionResponse>('/api/admin/workers/tasks/digest', {
      method: 'POST',
      body: JSON.stringify({ period }),
    }),
  dashboard: () => request<AdminDashboardResponse>('/api/admin/dashboard'),
  analytics: () => request<AdminAnalyticsResponse>('/api/admin/analytics'),
  listCategories: () => request<CategoryRecord[]>('/api/admin/categories'),
  createCategory: (payload: TaxonomyPayload) =>
    request<CategoryRecord>('/api/admin/categories', {
      method: 'POST',
      body: JSON.stringify({
        name: payload.name,
        slug: payload.slug,
        description: payload.description,
        cover_image: payload.coverImage,
        accent_color: payload.accentColor,
        seo_title: payload.seoTitle,
        seo_description: payload.seoDescription,
      }),
    }),
  updateCategory: (id: number, payload: TaxonomyPayload) =>
    request<CategoryRecord>(`/api/admin/categories/${id}`, {
      method: 'PATCH',
      body: JSON.stringify({
        name: payload.name,
        slug: payload.slug,
        description: payload.description,
        cover_image: payload.coverImage,
        accent_color: payload.accentColor,
        seo_title: payload.seoTitle,
        seo_description: payload.seoDescription,
      }),
    }),
  deleteCategory: (id: number) =>
    request<void>(`/api/admin/categories/${id}`, {
      method: 'DELETE',
    }),
  listTags: () => request<TagRecord[]>('/api/admin/tags'),
  createTag: (payload: TaxonomyPayload) =>
    request<TagRecord>('/api/admin/tags', {
      method: 'POST',
      body: JSON.stringify({
        name: payload.name,
        slug: payload.slug,
        description: payload.description,
        cover_image: payload.coverImage,
        accent_color: payload.accentColor,
        seo_title: payload.seoTitle,
        seo_description: payload.seoDescription,
      }),
    }),
  updateTag: (id: number, payload: TaxonomyPayload) =>
    request<TagRecord>(`/api/admin/tags/${id}`, {
      method: 'PATCH',
      body: JSON.stringify({
        name: payload.name,
        slug: payload.slug,
        description: payload.description,
        cover_image: payload.coverImage,
        accent_color: payload.accentColor,
        seo_title: payload.seoTitle,
        seo_description: payload.seoDescription,
      }),
    }),
  deleteTag: (id: number) =>
    request<void>(`/api/admin/tags/${id}`, {
      method: 'DELETE',
    }),
  getSiteSettings: () => request<AdminSiteSettingsResponse>('/api/admin/site-settings'),
  updateSiteSettings: (payload: SiteSettingsPayload) =>
    request<AdminSiteSettingsResponse>('/api/admin/site-settings', {
      method: 'PATCH',
      body: JSON.stringify(payload),
    }),
  reindexAi: () =>
    request<WorkerTaskActionResponse>('/api/admin/ai/reindex', {
      method: 'POST',
    }),
  testAiProvider: (provider: {
    id: string
    name: string
    provider: string
    api_base: string | null
    api_key: string | null
    chat_model: string | null
  }) =>
    request<AdminAiProviderTestResponse>('/api/admin/ai/test-provider', {
      method: 'POST',
      body: JSON.stringify({ provider }),
    }),
  testAiImageProvider: (provider: {
    provider: string
    api_base: string | null
    api_key: string | null
    image_model: string | null
  }) =>
    request<AdminAiImageProviderTestResponse>('/api/admin/ai/test-image-provider', {
      method: 'POST',
      body: JSON.stringify({
        provider: provider.provider,
        api_base: provider.api_base,
        api_key: provider.api_key,
        image_model: provider.image_model,
      }),
    }),
  uploadReviewCoverImage: (file: File) => {
    const formData = new FormData()
    formData.append('file', file, file.name)

    return request<AdminImageUploadResponse>('/api/admin/storage/review-cover', {
      method: 'POST',
      body: formData,
    })
  },
  testR2Storage: () =>
    request<AdminR2ConnectivityResponse>('/api/admin/storage/r2/test', {
      method: 'POST',
    }),
  listMediaObjects: (query?: { prefix?: string; limit?: number }) =>
    request<AdminMediaListResponse>(
      appendQueryParams('/api/admin/storage/media', {
        prefix: query?.prefix,
        limit: query?.limit,
      }),
    ),
  deleteMediaObject: (key: string) =>
    request<AdminMediaDeleteResponse>(
      `/api/admin/storage/media?key=${encodeURIComponent(key)}`,
      {
        method: 'DELETE',
      },
    ),
  uploadMediaObjects: (files: File[], options?: { prefix?: string }) => {
    const formData = new FormData()
    if (options?.prefix) {
      formData.append('prefix', options.prefix)
    }
    files.forEach((file) => {
      formData.append('files', file, file.name)
    })

    return request<AdminMediaUploadResponse>('/api/admin/storage/media', {
      method: 'POST',
      body: formData,
    })
  },
  batchDeleteMediaObjects: (keys: string[]) =>
    request<AdminMediaBatchDeleteResponse>('/api/admin/storage/media/batch-delete', {
      method: 'POST',
      body: JSON.stringify({ keys }),
    }),
  replaceMediaObject: (key: string, file: File) => {
    const formData = new FormData()
    formData.append('key', key)
    formData.append('file', file, file.name)

    return request<AdminMediaReplaceResponse>('/api/admin/storage/media/replace', {
      method: 'POST',
      body: formData,
    })
  },
  downloadMediaObject: (payload: MediaDownloadPayload) =>
    request<AdminMediaDownloadResponse>('/api/admin/storage/media/download', {
      method: 'POST',
      body: JSON.stringify({
        source_url: payload.sourceUrl,
        prefix: payload.prefix,
        target_format:
          payload.targetFormat && payload.targetFormat !== 'original' ? payload.targetFormat : null,
        title: payload.title,
        alt_text: payload.altText,
        caption: payload.caption,
        tags: payload.tags,
        notes: payload.notes,
        sync: payload.sync ?? false,
      }),
    }),
  updateMediaObjectMetadata: (payload: MediaAssetMetadataPayload) =>
    request<AdminMediaMetadataResponse>('/api/admin/storage/media/metadata', {
      method: 'PATCH',
      body: JSON.stringify({
        key: payload.key,
        title: payload.title,
        alt_text: payload.altText,
        caption: payload.caption,
        tags: payload.tags,
        notes: payload.notes,
      }),
    }),
  exportSiteBackup: () => request<SiteBackupDocument>('/api/admin/site-backup/export'),
  importSiteBackup: (payload: SiteBackupImportPayload) =>
    request<SiteBackupImportResponse>('/api/admin/site-backup/import', {
      method: 'POST',
      body: JSON.stringify(payload),
    }),
  generatePostMetadata: (markdown: string) =>
    request<AdminPostMetadataResponse>('/api/admin/ai/post-metadata', {
      method: 'POST',
      body: JSON.stringify({ markdown }),
    }),
  polishPostMarkdown: (markdown: string) =>
    request<AdminPostPolishResponse>('/api/admin/ai/polish-post', {
      method: 'POST',
      body: JSON.stringify({ markdown }),
    }),
  localizePostMarkdownImages: (payload: { markdown: string; prefix?: string | null }) =>
    request<AdminPostLocalizeImagesResponse>('/api/admin/posts/localize-images', {
      method: 'POST',
      body: JSON.stringify({
        markdown: payload.markdown,
        prefix: payload.prefix,
      }),
    }),
  polishReviewDescription: (payload: AdminReviewPolishRequest) =>
    request<AdminReviewPolishResponse>('/api/admin/ai/polish-review', {
      method: 'POST',
      body: JSON.stringify({
        title: payload.title,
        review_type: payload.reviewType,
        rating: payload.rating,
        review_date: payload.reviewDate,
        status: payload.status,
        tags: payload.tags,
        description: payload.description,
      }),
    }),
  generatePostCoverImage: (payload: AdminPostCoverImageRequest) =>
    request<AdminPostCoverImageResponse>('/api/admin/ai/post-cover', {
      method: 'POST',
      body: JSON.stringify({
        title: payload.title,
        description: payload.description,
        category: payload.category,
        tags: payload.tags,
        post_type: payload.postType,
        slug: payload.slug,
        markdown: payload.markdown,
      }),
    }),
  listPosts: (query?: PostListQuery) =>
    request<PostRecord[]>(
      appendQueryParams('/api/posts', {
        slug: query?.slug,
        category: query?.category,
        tag: query?.tag,
        search: query?.search,
        type: query?.postType,
        pinned: query?.pinned,
        status: query?.status,
        visibility: query?.visibility,
        listed_only: query?.listedOnly,
        include_private: query?.includePrivate ?? true,
        include_redirects: query?.includeRedirects ?? true,
        preview: query?.preview ?? true,
      }),
    ),
  listPostsPage: (query?: PostListQuery) =>
    request<PostPageResponse>(
      appendQueryParams('/api/posts/page', {
        slug: query?.slug,
        category: query?.category,
        tag: query?.tag,
        search: query?.search,
        type: query?.postType,
        pinned: query?.pinned,
        status: query?.status,
        visibility: query?.visibility,
        listed_only: query?.listedOnly,
        include_private: query?.includePrivate ?? true,
        include_redirects: query?.includeRedirects ?? true,
        preview: query?.preview ?? true,
        page: query?.page,
        page_size: query?.pageSize,
        sort_by: query?.sortBy,
        sort_order: query?.sortOrder,
      }),
    ),
  getPostBySlug: (slug: string) =>
    request<PostRecord>(`/api/posts/slug/${encodeURIComponent(slug)}?preview=true&include_private=true`),
  createPost: (payload: CreatePostPayload) =>
    request<MarkdownDocumentResponse>('/api/posts/markdown', {
      method: 'POST',
      body: JSON.stringify({
        title: payload.title,
        slug: payload.slug,
        description: payload.description,
        content: payload.content,
        category: payload.category,
        tags: payload.tags,
        post_type: payload.postType,
        image: payload.image,
        images: payload.images,
        pinned: payload.pinned,
        status: payload.status,
        visibility: payload.visibility,
        publish_at: payload.publishAt,
        unpublish_at: payload.unpublishAt,
        canonical_url: payload.canonicalUrl,
        noindex: payload.noindex,
        og_image: payload.ogImage,
        redirect_from: payload.redirectFrom,
        redirect_to: payload.redirectTo,
        published: payload.published,
      }),
    }),
  updatePost: (id: number, payload: UpdatePostPayload) =>
    request<PostRecord>(`/api/posts/${id}`, {
      method: 'PATCH',
      body: JSON.stringify({
        title: payload.title,
        slug: payload.slug,
        description: payload.description,
        content: payload.content,
        category: payload.category,
        tags: payload.tags,
        post_type: payload.postType,
        image: payload.image,
        images: payload.images,
        pinned: payload.pinned,
        status: payload.status,
        visibility: payload.visibility,
        publish_at: payload.publishAt,
        unpublish_at: payload.unpublishAt,
        canonical_url: payload.canonicalUrl,
        noindex: payload.noindex,
        og_image: payload.ogImage,
        redirect_from: payload.redirectFrom,
        redirect_to: payload.redirectTo,
      }),
    }),
  getPostMarkdown: (slug: string) =>
    request<MarkdownDocumentResponse>(`/api/posts/slug/${encodeURIComponent(slug)}/markdown`),
  importPosts: async (files: File[]) => {
    const formData = new FormData()

    files.forEach((file) => {
      formData.append('files', file, file.webkitRelativePath || file.name)
    })

    return request<MarkdownImportResponse>('/api/posts/markdown/import', {
      method: 'POST',
      body: formData,
    })
  },
  updatePostMarkdown: (slug: string, markdown: string) =>
    request<MarkdownDocumentResponse>(`/api/posts/slug/${encodeURIComponent(slug)}/markdown`, {
      method: 'PATCH',
      body: JSON.stringify({ markdown }),
    }),
  deletePost: (slug: string) =>
    request<MarkdownDeleteResponse>(`/api/posts/slug/${encodeURIComponent(slug)}/markdown`, {
      method: 'DELETE',
    }),
  listComments: (query?: CommentListQuery) =>
    request<CommentRecord[]>(
      appendQueryParams('/api/comments', {
        post_id: query?.postId,
        post_slug: query?.postSlug,
        scope: query?.scope,
        paragraph_key: query?.paragraphKey,
        approved: query?.approved,
      }),
    ),
  updateComment: (id: number, payload: UpdateCommentPayload) =>
    request<CommentRecord>(`/api/comments/${id}`, {
      method: 'PATCH',
      body: JSON.stringify(payload),
    }),
  deleteComment: (id: number) =>
    request<void>(`/api/comments/${id}`, {
      method: 'DELETE',
    }),
  listCommentBlacklist: () =>
    request<CommentBlacklistRecord[]>('/api/admin/comments/blacklist'),
  createCommentBlacklist: (payload: {
    matcher_type: 'ip' | 'email' | 'user_agent' | string
    matcher_value: string
    reason?: string | null
    active?: boolean
    expires_at?: string | null
  }) =>
    request<CommentBlacklistRecord>('/api/admin/comments/blacklist', {
      method: 'POST',
      body: JSON.stringify(payload),
    }),
  updateCommentBlacklist: (
    id: number,
    payload: {
      reason?: string | null
      active?: boolean
      expires_at?: string | null
      clear_expires_at?: boolean
    },
  ) =>
    request<CommentBlacklistRecord>(`/api/admin/comments/blacklist/${id}`, {
      method: 'PATCH',
      body: JSON.stringify(payload),
    }),
  deleteCommentBlacklist: (id: number) =>
    request<{ deleted: boolean; id: number }>(`/api/admin/comments/blacklist/${id}`, {
      method: 'DELETE',
    }),
  analyzeCommentPersona: (payload: {
    matcher_type: 'ip' | 'email' | 'user_agent' | string
    matcher_value: string
    from?: string | null
    to?: string | null
    limit?: number
  }) =>
    request<CommentPersonaAnalysisResponse>('/api/admin/comments/analyze', {
      method: 'POST',
      body: JSON.stringify(payload),
    }),
  listCommentPersonaAnalysisLogs: (query?: {
    matcher_type?: 'ip' | 'email' | 'user_agent' | string
    matcher_value?: string
    limit?: number
  }) =>
    request<CommentPersonaAnalysisLogRecord[]>(
      appendQueryParams('/api/admin/comments/analyze/logs', {
        matcher_type: query?.matcher_type,
        matcher_value: query?.matcher_value,
        limit: query?.limit,
      }),
    ),
  listFriendLinks: (query?: FriendLinkListQuery) =>
    request<FriendLinkRecord[]>(
      appendQueryParams('/api/friend_links', {
        status: query?.status,
        category: query?.category,
      }),
    ),
  createFriendLink: (payload: FriendLinkPayload) =>
    request<FriendLinkRecord>('/api/friend_links', {
      method: 'POST',
      body: JSON.stringify({
        siteName: payload.siteName,
        siteUrl: payload.siteUrl,
        avatarUrl: payload.avatarUrl,
        description: payload.description,
        category: payload.category,
        status: payload.status,
      }),
    }),
  updateFriendLink: (id: number, payload: FriendLinkPayload) =>
    request<FriendLinkRecord>(`/api/friend_links/${id}`, {
      method: 'PATCH',
      body: JSON.stringify({
        site_name: payload.siteName,
        site_url: payload.siteUrl,
        avatar_url: payload.avatarUrl,
        description: payload.description,
        category: payload.category,
        status: payload.status,
      }),
    }),
  deleteFriendLink: (id: number) =>
    request<void>(`/api/friend_links/${id}`, {
      method: 'DELETE',
    }),
  listReviews: () => request<ReviewRecord[]>('/api/reviews'),
  createReview: (payload: CreateReviewPayload) =>
    request<ReviewRecord>('/api/reviews', {
      method: 'POST',
      body: JSON.stringify(payload),
    }),
  updateReview: (id: number, payload: UpdateReviewPayload) =>
    request<ReviewRecord>(`/api/reviews/${id}`, {
      method: 'PUT',
      body: JSON.stringify(payload),
    }),
  deleteReview: (id: number) =>
    request<void>(`/api/reviews/${id}`, {
      method: 'DELETE',
    }),
}
