/**
 * Convex Storage Helpers
 *
 * Server-side helpers for uploading/downloading/deleting files via Convex storage.
 * Used by Next.js API routes that previously used Supabase Storage.
 *
 * Pattern:
 *   1. Generate an upload URL via Convex mutation
 *   2. POST the file buffer to that URL (returns a storageId)
 *   3. Save metadata via Convex mutation
 *   4. Query the URL when needed
 */

import { convexMutation, convexQuery } from "@/lib/convex-client";

export interface ConvexUploadResult {
  storageId: string;
  fileName: string;
  fileType: string;
  fileSize: number;
  bucket: string;
}

export interface ConvexFileRecord {
  storageId: string;
  fileName: string;
  fileType: string;
  fileSize: number;
}

/**
 * Upload a file buffer to Convex storage and save its metadata.
 *
 * @returns The storageId and metadata, or null if Convex is not configured.
 * @throws If the upload or metadata save fails.
 */
export async function uploadToConvexStorage(opts: {
  buffer: Buffer | Uint8Array;
  fileName: string;
  fileType: string;
  fileSize: number;
  bucket: string;
  entityId?: string;
  entityType?: string;
  uploadedBy?: string;
}): Promise<ConvexUploadResult> {
  // 1. Generate upload URL
  const uploadUrl = await convexMutation(
    "storage/mutations:generateUploadUrl",
    {},
  );
  if (!uploadUrl) {
    throw new Error(
      "Failed to generate Convex upload URL (Convex not configured?)",
    );
  }

  // 2. Upload file to the URL
  const uploadResponse = await fetch(uploadUrl as string, {
    method: "POST",
    headers: { "Content-Type": opts.fileType },
    body: opts.buffer,
  });

  if (!uploadResponse.ok) {
    const text = await uploadResponse.text().catch(() => "unknown error");
    throw new Error(
      `Convex storage upload failed: ${uploadResponse.status} ${text}`,
    );
  }

  const { storageId } = await uploadResponse.json();
  if (!storageId) {
    throw new Error("Convex storage upload did not return a storageId");
  }

  // 3. Save metadata
  await convexMutation("storage/mutations:saveFileMetadata", {
    storageId,
    fileName: opts.fileName,
    fileType: opts.fileType,
    fileSize: opts.fileSize,
    bucket: opts.bucket,
    entityId: opts.entityId,
    entityType: opts.entityType,
    uploadedBy: opts.uploadedBy,
  });

  return {
    storageId,
    fileName: opts.fileName,
    fileType: opts.fileType,
    fileSize: opts.fileSize,
    bucket: opts.bucket,
  };
}

/**
 * Get the public URL for a file in Convex storage.
 * The storageId string is passed directly — the Convex client handles ID coercion.
 */
export async function getConvexFileUrl(
  storageId: string,
): Promise<string | null> {
  // convexQuery accepts Record<string, unknown>; storageId coerced by Convex runtime
  const url = await convexQuery("storage/queries:getFileUrl", {
    storageId: storageId as unknown,
  } as Record<string, unknown>);
  return (url as string) ?? null;
}

/**
 * Download a file from Convex storage as a Buffer.
 */
export async function downloadFromConvexStorage(storageId: string): Promise<{
  buffer: Buffer;
  contentType: string;
} | null> {
  const url = await getConvexFileUrl(storageId);
  if (!url) return null;

  const response = await fetch(url);
  if (!response.ok) return null;

  const arrayBuffer = await response.arrayBuffer();
  const contentType =
    response.headers.get("content-type") || "application/octet-stream";

  return {
    buffer: Buffer.from(arrayBuffer),
    contentType,
  };
}

/**
 * Delete a file from Convex storage (both the blob and its metadata).
 */
export async function deleteFromConvexStorage(
  storageId: string,
): Promise<void> {
  await convexMutation("storage/mutations:deleteFile", {
    storageId: storageId as unknown,
  } as Record<string, unknown>);
}

/**
 * Get all files associated with a specific entity.
 */
export async function getConvexFilesByEntity(
  entityType: string,
  entityId: string,
): Promise<ConvexFileRecord[]> {
  const files = await convexQuery("storage/queries:getFilesByAssociation", {
    entityType,
    entityId,
  });
  return (files as ConvexFileRecord[]) ?? [];
}
