export type CaseRecord = {
  caseId: string;
  status: string;
  current_step: string | null;
  created_at: string;
  analysis_result?: AnalysisResult | null;
  landmarks_data?: LandmarksResult | null;
  analysis_data?: RecalculationResult | null;
  body_scan_path?: string | null;
  body_scan_url?: string | null;
  body_scan_metadata?: BodyScanMetadata | null;
};

export type BodyScanMetadata = {
  total_height_mm?: number;
  shoulder_width_mm?: number;
  chest_width_mm?: number;
  chest_depth_mm?: number;
  waist_width_mm?: number;
  waist_depth_mm?: number;
  hip_width_mm?: number;
  brace_coverage_height_mm?: number;
  file_format?: string;
  vertex_count?: number;
  filename?: string;
};

export type CobbAngles = {
  PT?: number;
  MT?: number;
  TL?: number;
};

export type RigoClassification = {
  type: string;
  description: string;
  curve_pattern?: string;
};

export type DeformationZone = {
  zone: string;
  patch: [number, number];
  deform_mm: number;
  reason: string;
};

export type DeformationReport = {
  patch_grid: string;
  deformations: number[][];
  zones: DeformationZone[];
};

export type AnalysisResult = {
  experiment?: string;
  model?: string;
  vertebrae_detected?: number;
  cobb_angles?: CobbAngles;
  curve_type?: string;
  rigo_classification?: RigoClassification;
  mesh_info?: {
    vertices?: number;
    faces?: number;
  };
  deformation_report?: DeformationReport;
  outputs?: Record<string, { s3Key: string; url: string }>;
  processing_time_ms?: number;
};

export type BraceOutput = {
  filename: string;
  type: "stl" | "ply" | "obj" | "image" | "json" | "other";
  s3Key: string;
  size: number;
  url: string;
  expiresIn: number;
};

export type BraceOutputsResponse = {
  caseId: string;
  status: string;
  analysis: AnalysisResult | null;
  outputs: BraceOutput[];
};

export type GenerateBraceResponse = {
  caseId: string;
  status: string;
  experiment: string;
  model: string;
  vertebrae_detected: number;
  cobb_angles: CobbAngles;
  curve_type: string;
  rigo_classification: RigoClassification;
  mesh: {
    vertices: number;
    faces: number;
  };
  outputs: Record<string, { s3Key: string; url: string }>;
  processing_time_ms: number;
  deformation_report?: DeformationReport;
};

// API Base URL
// - In production (Docker): empty string uses relative URLs with /api prefix
// - In development: set VITE_API_BASE=http://localhost:8001 in .env.local
const API_BASE = import.meta.env.VITE_API_BASE ?? "";

// API prefix for relative URLs (when API_BASE is empty or doesn't include /api)
const API_PREFIX = "/api";

// File server base URL (same as API base for dev server)
const FILE_BASE = API_BASE.replace(/\/api\/?$/, '');

/**
 * Convert relative file URLs to absolute URLs
 * e.g., "/files/outputs/..." -> "http://localhost:8001/files/outputs/..."
 */
export function toAbsoluteFileUrl(relativeUrl: string | undefined | null): string | undefined {
  if (!relativeUrl) return undefined;
  if (relativeUrl.startsWith('http://') || relativeUrl.startsWith('https://')) {
    return relativeUrl; // Already absolute
  }
  return `${FILE_BASE}${relativeUrl.startsWith('/') ? '' : '/'}${relativeUrl}`;
}

async function safeFetch<T>(path: string, init?: RequestInit): Promise<T> {
  const base = API_BASE ? API_BASE.replace(/\/+$/, "") : "";
  const normalizedPath = path.startsWith("/") ? path : `/${path}`;
  // If no base URL, use API_PREFIX for relative URLs (production with nginx proxy)
  const prefix = base ? "" : API_PREFIX;
  const url = `${base}${prefix}${normalizedPath}`;

  const res = await fetch(url, init);
  const text = await res.text().catch(() => "");

  if (!res.ok) {
    throw new Error(`Request failed: ${res.status} ${res.statusText}${text ? ` :: ${text}` : ""}`);
  }

  return text ? (JSON.parse(text) as T) : ({} as T);
}

/**
 * Supports common backend shapes:
 *  - Array: [ {caseId...}, ... ]
 *  - Object: { cases: [...], nextToken: "..." }
 *  - Object: { items: [...], nextToken: "..." }
 */
function normalizeCasesResponse(
  json: any
): { cases: CaseRecord[]; nextToken?: string | null } {
  if (Array.isArray(json)) return { cases: json, nextToken: null };

  const cases = (json?.cases ?? json?.items ?? []) as CaseRecord[];
  const nextToken = (json?.nextToken ?? json?.next_token ?? json?.nextCursor ?? json?.next_cursor ?? null) as
    | string
    | null;

  return { cases: Array.isArray(cases) ? cases : [], nextToken };
}

/**
 * Fetch ALL cases, following nextToken if backend paginates.
 * Uses query param `?nextToken=` (common pattern).
 */
export async function fetchCases(): Promise<CaseRecord[]> {
  const all: CaseRecord[] = [];
  let nextToken: string | null = null;

  // Safety guard to prevent infinite loops if backend misbehaves
  const MAX_PAGES = 50;

  for (let page = 0; page < MAX_PAGES; page++) {
    const path = nextToken ? `/cases?nextToken=${encodeURIComponent(nextToken)}` : `/cases`;
    const json = await safeFetch<any>(path);

    const normalized = normalizeCasesResponse(json);
    all.push(...normalized.cases);

    if (!normalized.nextToken) break;
    nextToken = normalized.nextToken;
  }

  // Sort newest-first
  all.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
  return all;
}

export async function fetchCase(caseId: string): Promise<CaseRecord | null> {
  try {
    const result = await safeFetch<CaseRecord>(`/cases/${encodeURIComponent(caseId)}`);
    
    // Convert relative URLs to absolute URLs for braces data
    if (result?.analysis_result) {
      const ar = result.analysis_result as any;
      // Convert both braces format URLs
      if (ar.braces) {
        if (ar.braces.regular?.outputs) {
          ar.braces.regular.outputs = {
            glb: toAbsoluteFileUrl(ar.braces.regular.outputs.glb),
            stl: toAbsoluteFileUrl(ar.braces.regular.outputs.stl),
            json: toAbsoluteFileUrl(ar.braces.regular.outputs.json),
          };
        }
        if (ar.braces.vase?.outputs) {
          ar.braces.vase.outputs = {
            glb: toAbsoluteFileUrl(ar.braces.vase.outputs.glb),
            stl: toAbsoluteFileUrl(ar.braces.vase.outputs.stl),
            json: toAbsoluteFileUrl(ar.braces.vase.outputs.json),
          };
        }
      }
      // Convert single brace format URLs
      if (ar.brace?.outputs) {
        ar.brace.outputs = {
          stl: toAbsoluteFileUrl(ar.brace.outputs.stl),
          ply: toAbsoluteFileUrl(ar.brace.outputs.ply),
          visualization: toAbsoluteFileUrl(ar.brace.outputs.visualization),
          landmarks: toAbsoluteFileUrl(ar.brace.outputs.landmarks),
        };
      }
    }
    
    return result;
  } catch {
    return null;
  }
}

export async function createCase(body: { notes?: string } = {}): Promise<{ caseId: string }> {
  return await safeFetch<{ caseId: string }>(`/cases`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
}

/**
 * Get a presigned URL for uploading an X-ray image to S3
 */
export async function getUploadUrl(
  caseId: string,
  filename: string,
  contentType: string
): Promise<{ url: string; s3Key: string }> {
  return await safeFetch<{ url: string; s3Key: string }>(
    `/cases/${encodeURIComponent(caseId)}/upload-url`,
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ filename, contentType }),
    }
  );
}

/**
 * Upload a file directly to S3 using a presigned URL
 */
export async function uploadToS3(presignedUrl: string, file: File): Promise<void> {
  const response = await fetch(presignedUrl, {
    method: "PUT",
    body: file,
    headers: {
      "Content-Type": file.type,
    },
  });

  if (!response.ok) {
    throw new Error(`Upload failed: ${response.status} ${response.statusText}`);
  }
}

/**
 * Create a case and upload an X-ray without starting processing.
 */
export async function createCaseAndUploadXray(
  file: File,
  options?: { notes?: string }
): Promise<{ caseId: string }> {
  const { caseId } = await createCase({
    notes: options?.notes ?? `X-ray upload: ${file.name}`,
  });

  const { url: uploadUrl } = await getUploadUrl(caseId, "ap.jpg", file.type);
  await uploadToS3(uploadUrl, file);

  return { caseId };
}

/**
 * Invoke the brace generator Lambda for a case
 */
export async function generateBrace(
  caseId: string,
  options?: { experiment?: string; config?: Record<string, unknown> }
): Promise<GenerateBraceResponse> {
  return await safeFetch<GenerateBraceResponse>(
    `/cases/${encodeURIComponent(caseId)}/generate-brace`,
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        caseId,
        experiment: options?.experiment ?? "experiment_3",
        config: options?.config,
      }),
    }
  );
}

/**
 * Get brace generation outputs for a case (with presigned URLs)
 */
export async function getBraceOutputs(caseId: string): Promise<BraceOutputsResponse> {
  return await safeFetch<BraceOutputsResponse>(
    `/cases/${encodeURIComponent(caseId)}/brace-outputs`
  );
}

/**
 * Get a presigned download URL for a case asset (e.g., X-ray)
 */
export async function getDownloadUrl(
  caseId: string,
  assetType: "xray" | "landmarks" | "measurements"
): Promise<{ url: string }> {
  return await safeFetch<{ url: string }>(
    `/cases/${encodeURIComponent(caseId)}/download-url?type=${assetType}`
  );
}

/**
 * Full workflow: Create case -> Upload X-ray -> Generate brace
 */
export async function analyzeXray(
  file: File,
  options?: { experiment?: string; config?: Record<string, unknown> }
): Promise<{ caseId: string; result: GenerateBraceResponse }> {
  // Step 1: Create a new case
  const { caseId } = await createCase({ notes: `X-ray analysis: ${file.name}` });
  console.log(`Created case: ${caseId}`);

  // Step 2: Get presigned URL for upload
  const { url: uploadUrl } = await getUploadUrl(caseId, "ap.jpg", file.type);
  console.log(`Got upload URL for case: ${caseId}`);

  // Step 3: Upload file to S3
  await uploadToS3(uploadUrl, file);
  console.log(`Uploaded X-ray to S3 for case: ${caseId}`);

  // Step 4: Generate brace
  const result = await generateBrace(caseId, options);
  console.log(`Brace generation complete for case: ${caseId}`);

  return { caseId, result };
}

/**
 * Delete a case and all associated files
 */
export async function deleteCase(caseId: string): Promise<{ message: string }> {
  return await safeFetch<{ message: string }>(
    `/cases/${encodeURIComponent(caseId)}`,
    { method: "DELETE" }
  );
}

// ============================================
// PIPELINE DEV API - New Stage-based endpoints
// ============================================

export type VertebraData = {
  level: string;
  detected: boolean;
  scoliovis_data: {
    centroid_px: [number, number] | null;
    corners_px: [number, number][] | null;
    orientation_deg: number | null;
    confidence: number;
  };
  manual_override: {
    enabled: boolean;
    centroid_px: [number, number] | null;
    corners_px: [number, number][] | null;
    orientation_deg: number | null;
    confidence: number | null;
    notes: string | null;
  };
  final_values: {
    centroid_px: [number, number] | null;
    corners_px: [number, number][] | null;
    orientation_deg: number | null;
    confidence: number;
    source: 'scoliovis' | 'manual' | 'undetected';
  };
};

export type VertebraeStructure = {
  all_levels: string[];
  detected_count: number;
  total_count: number;
  vertebrae: VertebraData[];
  manual_edit_instructions: {
    to_override: string;
    final_values_rule: string;
  };
};

export type LandmarksResult = {
  case_id: string;
  status: string;
  input: {
    image_dimensions: { width: number; height: number };
    pixel_spacing_mm: number | null;
  };
  detection_quality: {
    vertebrae_count: number;
    average_confidence: number;
  };
  cobb_angles: {
    PT: number;
    MT: number;
    TL: number;
    max: number;
    PT_severity: string;
    MT_severity: string;
    TL_severity: string;
  };
  rigo_classification: RigoClassification;
  curve_type: string;
  vertebrae_structure: VertebraeStructure;
  visualization_path?: string;
  visualization_url?: string;
  json_path?: string;
  json_url?: string;
  processing_time_ms: number;
};

export type RecalculationResult = {
  case_id: string;
  status: string;
  cobb_angles: {
    PT: number;
    MT: number;
    TL: number;
    max: number;
    PT_severity: string;
    MT_severity: string;
    TL_severity: string;
  };
  rigo_classification: RigoClassification;
  curve_type: string;
  apex_indices: number[];
  vertebrae_used: number;
  processing_time_ms: number;
};

/**
 * Stage 1: Upload X-ray and detect landmarks
 */
export async function uploadXrayForCase(caseId: string, file: File): Promise<{ filename: string; path: string }> {
  const formData = new FormData();
  formData.append('file', file);
  
  // Use API_BASE if set, otherwise use /api prefix for production
  const base = API_BASE ? API_BASE.replace(/\/+$/, "") : API_PREFIX;
  const res = await fetch(`${base}/cases/${encodeURIComponent(caseId)}/upload`, {
    method: 'POST',
    body: formData,
  });
  
  if (!res.ok) {
    const text = await res.text();
    throw new Error(`Upload failed: ${res.status} ${text}`);
  }
  
  return res.json();
}

/**
 * Stage 1: Detect landmarks (no brace generation)
 */
export async function detectLandmarks(caseId: string): Promise<LandmarksResult> {
  return await safeFetch<LandmarksResult>(
    `/cases/${encodeURIComponent(caseId)}/detect-landmarks`,
    { method: 'POST' }
  );
}

/**
 * Stage 1: Update landmarks (manual edits)
 */
export async function updateLandmarks(
  caseId: string, 
  landmarksData: VertebraeStructure
): Promise<{ caseId: string; status: string }> {
  return await safeFetch<{ caseId: string; status: string }>(
    `/cases/${encodeURIComponent(caseId)}/landmarks`,
    {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ landmarks_data: landmarksData }),
    }
  );
}

/**
 * Stage 1->2: Approve landmarks and move to analysis
 */
export async function approveLandmarks(
  caseId: string,
  updatedLandmarks?: VertebraeStructure
): Promise<{ caseId: string; status: string; next_step: string }> {
  return await safeFetch<{ caseId: string; status: string; next_step: string }>(
    `/cases/${encodeURIComponent(caseId)}/approve-landmarks`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ updated_landmarks: updatedLandmarks }),
    }
  );
}

/**
 * Stage 2: Recalculate Cobb angles and Rigo from landmarks
 */
export async function recalculateAnalysis(caseId: string): Promise<RecalculationResult> {
  return await safeFetch<RecalculationResult>(
    `/cases/${encodeURIComponent(caseId)}/recalculate`,
    { method: 'POST' }
  );
}

/**
 * Stage 3: Generate brace from approved landmarks
 */
export async function generateBraceFromLandmarks(
  caseId: string,
  options?: { experiment?: string }
): Promise<GenerateBraceResponse> {
  return await safeFetch<GenerateBraceResponse>(
    `/cases/${encodeURIComponent(caseId)}/generate-brace`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        experiment: options?.experiment ?? 'experiment_9',
      }),
    }
  );
}

/**
 * Stage 3: Update brace markers (manual edits)
 */
export async function updateMarkers(
  caseId: string,
  markersData: Record<string, unknown>
): Promise<{ caseId: string; status: string }> {
  return await safeFetch<{ caseId: string; status: string }>(
    `/cases/${encodeURIComponent(caseId)}/markers`,
    {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ markers_data: markersData }),
    }
  );
}

/**
 * Generate GLB brace with markers
 */
export async function generateGlbBrace(
  caseId: string,
  options?: { 
    rigoType?: string;
    templateType?: 'regular' | 'vase';
  }
): Promise<{
  caseId: string;
  rigoType: string;
  templateType: string;
  outputs: { glb?: string; stl?: string; json?: string };
  markers: Record<string, number[]>;
  pressureZones: Array<{
    name: string;
    marker_name: string;
    position: number[];
    zone_type: string;
    direction: string;
    depth_mm: number;
  }>;
  meshStats: { vertices: number; faces: number };
}> {
  const formData = new FormData();
  if (options?.rigoType) formData.append('rigo_type', options.rigoType);
  if (options?.templateType) formData.append('template_type', options.templateType);
  formData.append('case_id', caseId);
  
  return await safeFetch(
    `/cases/${encodeURIComponent(caseId)}/generate-glb`,
    {
      method: 'POST',
      body: formData,
    }
  );
}

/**
 * Generate both brace types (regular + vase) for comparison
 */
export type BothBracesResponse = {
  caseId: string;
  rigoType: string;
  cobbAngles: { PT: number; MT: number; TL: number };
  bodyScanUsed: boolean;
  braces: {
    regular?: {
      outputs: { glb?: string; stl?: string; json?: string };
      markers: Record<string, number[]>;
      pressureZones: Array<{
        name: string;
        marker_name: string;
        position: number[];
        zone_type: string;
        direction: string;
        depth_mm: number;
      }>;
      meshStats: { vertices: number; faces: number };
    };
    vase?: {
      outputs: { glb?: string; stl?: string; json?: string };
      markers: Record<string, number[]>;
      pressureZones: Array<{
        name: string;
        marker_name: string;
        position: number[];
        zone_type: string;
        direction: string;
        depth_mm: number;
      }>;
      meshStats: { vertices: number; faces: number };
      error?: string;
    };
  };
};

export async function generateBothBraces(
  caseId: string,
  options?: { rigoType?: string }
): Promise<BothBracesResponse> {
  const formData = new FormData();
  if (options?.rigoType) formData.append('rigo_type', options.rigoType);
  formData.append('case_id', caseId);
  
  const result = await safeFetch<BothBracesResponse>(
    `/cases/${encodeURIComponent(caseId)}/generate-both-braces`,
    {
      method: 'POST',
      body: formData,
    }
  );
  
  // Convert relative URLs to absolute URLs for 3D loaders
  if (result.braces?.regular?.outputs) {
    result.braces.regular.outputs = {
      glb: toAbsoluteFileUrl(result.braces.regular.outputs.glb),
      stl: toAbsoluteFileUrl(result.braces.regular.outputs.stl),
      json: toAbsoluteFileUrl(result.braces.regular.outputs.json),
    };
  }
  if (result.braces?.vase?.outputs) {
    result.braces.vase.outputs = {
      glb: toAbsoluteFileUrl(result.braces.vase.outputs.glb),
      stl: toAbsoluteFileUrl(result.braces.vase.outputs.stl),
      json: toAbsoluteFileUrl(result.braces.vase.outputs.json),
    };
  }
  
  return result;
}

/**
 * Get case assets (uploaded files and outputs)
 */
export async function getCaseAssets(caseId: string): Promise<{
  caseId: string;
  assets: {
    uploads: { filename: string; url: string }[];
    outputs: { filename: string; url: string }[];
  };
}> {
  const result = await safeFetch<{
    caseId: string;
    assets: {
      uploads: { filename: string; url: string }[];
      outputs: { filename: string; url: string }[];
    };
  }>(`/cases/${encodeURIComponent(caseId)}/assets`);
  
  // Convert relative URLs to absolute
  if (result.assets?.uploads) {
    result.assets.uploads = result.assets.uploads.map(f => ({
      ...f,
      url: toAbsoluteFileUrl(f.url) || f.url
    }));
  }
  if (result.assets?.outputs) {
    result.assets.outputs = result.assets.outputs.map(f => ({
      ...f,
      url: toAbsoluteFileUrl(f.url) || f.url
    }));
  }
  
  return result;
}

// ==============================================
// BODY SCAN API (Stage 3)
// ==============================================

export type BodyScanResponse = {
  caseId: string;
  has_body_scan: boolean;
  body_scan: {
    path: string;
    url: string;
    metadata: BodyScanMetadata;
  } | null;
};

export type BodyScanUploadResponse = {
  caseId: string;
  status: string;
  body_scan: {
    path: string;
    url: string;
    metadata: BodyScanMetadata;
  };
};

/**
 * Upload body scan (STL/OBJ/PLY)
 */
export async function uploadBodyScan(
  caseId: string,
  file: File
): Promise<BodyScanUploadResponse> {
  const formData = new FormData();
  formData.append('file', file);

  // Use API_BASE if set, otherwise use /api prefix for production
  const base = API_BASE ? API_BASE.replace(/\/+$/, "") : API_PREFIX;
  const response = await fetch(`${base}/cases/${encodeURIComponent(caseId)}/body-scan`, {
    method: 'POST',
    body: formData,
  });

  if (!response.ok) {
    const err = await response.json().catch(() => ({ message: 'Upload failed' }));
    throw new Error(err.message || 'Failed to upload body scan');
  }

  return response.json();
}

/**
 * Get body scan info
 */
export async function getBodyScan(caseId: string): Promise<BodyScanResponse> {
  return await safeFetch(`/cases/${encodeURIComponent(caseId)}/body-scan`);
}

/**
 * Delete body scan
 */
export async function deleteBodyScan(
  caseId: string
): Promise<{ caseId: string; status: string; message: string }> {
  return await safeFetch(
    `/cases/${encodeURIComponent(caseId)}/body-scan`,
    { method: 'DELETE' }
  );
}

/**
 * Skip body scan stage and proceed to brace generation
 */
export async function skipBodyScan(
  caseId: string
): Promise<{ caseId: string; status: string; message: string }> {
  return await safeFetch(
    `/cases/${encodeURIComponent(caseId)}/skip-body-scan`,
    { method: 'POST' }
  );
}

/**
 * Upload a modified brace file to the case outputs
 * Used when the user modifies a brace in the inline editor
 */
export async function uploadModifiedBrace(
  caseId: string,
  braceType: 'regular' | 'vase',
  fileType: 'stl' | 'glb',
  blob: Blob,
  transformParams?: Record<string, unknown>
): Promise<{ 
  caseId: string; 
  status: string; 
  output: { 
    filename: string; 
    url: string; 
    s3Key: string;
  };
}> {
  const filename = `${braceType}_modified.${fileType}`;
  const formData = new FormData();
  formData.append('file', blob, filename);
  formData.append('brace_type', braceType);
  formData.append('file_type', fileType);
  if (transformParams) {
    formData.append('transform_params', JSON.stringify(transformParams));
  }

  // Use API_BASE if set, otherwise use /api prefix for production
  const base = API_BASE ? API_BASE.replace(/\/+$/, "") : API_PREFIX;
  const response = await fetch(
    `${base}/cases/${encodeURIComponent(caseId)}/upload-modified-brace`,
    {
      method: 'POST',
      body: formData,
    }
  );

  if (!response.ok) {
    const err = await response.json().catch(() => ({ message: 'Upload failed' }));
    throw new Error(err.message || 'Failed to upload modified brace');
  }

  return response.json();
}

/**
 * Get presigned URL for uploading modified brace to case storage
 */
export async function getModifiedBraceUploadUrl(
  caseId: string,
  braceType: 'regular' | 'vase',
  fileType: 'stl' | 'glb'
): Promise<{ url: string; s3Key: string; contentType: string }> {
  const filename = `${braceType}_modified.${fileType}`;
  return await safeFetch<{ url: string; s3Key: string; contentType: string }>(
    `/cases/${encodeURIComponent(caseId)}/upload-url`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ 
        uploadType: 'modified_brace',
        braceType,
        fileType,
        filename,
        contentType: fileType === 'stl' ? 'application/octet-stream' : 'model/gltf-binary',
      }),
    }
  );
}

/**
 * Upload blob to S3 using presigned URL
 */
export async function uploadBlobToS3(presignedUrl: string, blob: Blob, contentType: string): Promise<void> {
  const response = await fetch(presignedUrl, {
    method: 'PUT',
    body: blob,
    headers: {
      'Content-Type': contentType,
    },
  });

  if (!response.ok) {
    throw new Error(`Upload failed: ${response.status} ${response.statusText}`);
  }
}
