import { MAX_BOOK_BYTES, validateBookFile } from "../domain/file-validation.ts";
import { apiJson } from "./http.ts";

export interface PublicUploadSession {
  id: string;
  fileName: string;
  mimeType: string;
  format: "epub" | "pdf";
  totalBytes: number;
  partSize: number;
  partCount: number;
  uploadedParts: Array<{ partNumber: number; etag: string }>;
  status: "created" | "uploading" | "completing" | "complete" | "aborted";
  expiresAt: string;
}

export async function inspectBookFile(file: File) {
  return validateBookFile({
    name: file.name,
    size: file.size,
    mimeType: file.type,
    header: new Uint8Array(await file.slice(0, 16).arrayBuffer()),
  });
}

export async function createUpload(file: File): Promise<PublicUploadSession> {
  if (file.size > MAX_BOOK_BYTES) throw new Error("文件超过 200 MB。");
  const result = await apiJson<{ upload: PublicUploadSession }>("/api/uploads", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({
      fileName: file.name,
      mimeType: file.type || "application/octet-stream",
      totalBytes: file.size,
    }),
  });
  return result.upload;
}

export async function resumeUpload(
  id: string,
): Promise<PublicUploadSession> {
  return (await apiJson<{ upload: PublicUploadSession }>(`/api/uploads/${id}`)).upload;
}

export async function uploadBookFile(
  file: File,
  initial: PublicUploadSession,
  onProgress: (progress: {
    uploadedBytes: number;
    totalBytes: number;
    partNumber: number;
    partCount: number;
  }) => void,
): Promise<{ id: string; title: string; author: string; status: string }> {
  const alreadyUploaded = new Set(
    initial.uploadedParts.map((part) => part.partNumber),
  );
  let uploadedBytes = 0;
  for (let partNumber = 1; partNumber <= initial.partCount; partNumber += 1) {
    const start = (partNumber - 1) * initial.partSize;
    const end = Math.min(file.size, start + initial.partSize);
    if (alreadyUploaded.has(partNumber)) {
      uploadedBytes += end - start;
      onProgress({ uploadedBytes, totalBytes: file.size, partNumber, partCount: initial.partCount });
      continue;
    }
    await apiJson<{ part: { partNumber: number; etag: string } }>(
      `/api/uploads/${initial.id}/parts/${partNumber}`,
      {
        method: "PUT",
        headers: { "content-type": "application/octet-stream" },
        body: file.slice(start, end),
      },
    );
    uploadedBytes += end - start;
    onProgress({ uploadedBytes, totalBytes: file.size, partNumber, partCount: initial.partCount });
  }
  return (
    await apiJson<{ book: { id: string; title: string; author: string; status: string } }>(
      `/api/uploads/${initial.id}/complete`,
      { method: "POST" },
    )
  ).book;
}

export async function abortUpload(id: string): Promise<void> {
  await apiJson(`/api/uploads/${id}`, { method: "DELETE" });
}
