import type { BookRepository } from "./repositories/books.ts";

const MAX_COVER_BYTES = 5 * 1024 * 1024;

interface CoverBucket {
  put(key: string, value: Uint8Array, options?: { httpMetadata?: { contentType?: string } }): Promise<unknown>;
  get(key: string): Promise<{
    body: BodyInit | null;
    size: number;
    httpEtag?: string;
    httpMetadata?: { contentType?: string };
  } | null>;
  delete(key: string): Promise<void>;
}

type CoverRepository = Pick<BookRepository, "cover" | "setCover" | "removeCover">;

const formats = {
  "image/jpeg": { extension: "jpg", valid: (bytes: Uint8Array) => bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff },
  "image/png": { extension: "png", valid: (bytes: Uint8Array) => bytes.length >= 8 && [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a].every((value, index) => bytes[index] === value) },
  "image/webp": { extension: "webp", valid: (bytes: Uint8Array) => bytes.length >= 12 && new TextDecoder().decode(bytes.slice(0, 4)) === "RIFF" && new TextDecoder().decode(bytes.slice(8, 12)) === "WEBP" },
} as const;

function responseError(status: number, code: string, message: string): Response {
  return Response.json({ error: { code, message } }, { status });
}

export function createCoverApi(dependencies: {
  repository: CoverRepository;
  bucket: CoverBucket;
  assertStorageAdmission(bytes: number, reclaimableBytes?: number): Promise<void>;
  idFactory?: () => string;
  now?: () => Date;
}) {
  const idFactory = dependencies.idFactory ?? (() => crypto.randomUUID());
  const now = dependencies.now ?? (() => new Date());

  return {
    async get(bookId: string): Promise<Response> {
      try {
        const cover = await dependencies.repository.cover(bookId);
        if (!cover?.objectKey) return responseError(404, "cover_not_found", "找不到这本书的封面。");
        const object = await dependencies.bucket.get(cover.objectKey);
        if (!object) return responseError(404, "cover_not_found", "封面文件已经失效，请重新上传。");
        return new Response(object.body, {
          headers: {
            "content-type": object.httpMetadata?.contentType ?? "application/octet-stream",
            "content-length": String(object.size),
            "cache-control": "public, max-age=300",
            etag: object.httpEtag ?? "",
          },
        });
      } catch (cause) {
        return responseError(400, "cover_read_failed", cause instanceof Error ? cause.message : "封面无法加载。");
      }
    },

    async put(request: Request, bookId: string): Promise<Response> {
      const contentType = request.headers.get("content-type")?.split(";", 1)[0].trim().toLowerCase() ?? "";
      const format = formats[contentType as keyof typeof formats];
      if (!format) return responseError(415, "unsupported_cover", "封面只支持 JPEG、PNG 或 WebP。");
      const declaredSize = Number(request.headers.get("content-length") ?? 0);
      if (declaredSize > MAX_COVER_BYTES) return responseError(413, "cover_too_large", "封面不能超过 5 MB。");
      try {
        const current = await dependencies.repository.cover(bookId);
        if (!current) return responseError(404, "book_not_found", "找不到这本书。");
        const bytes = new Uint8Array(await request.arrayBuffer());
        if (!bytes.length) return responseError(400, "empty_cover", "封面不能为空。");
        if (bytes.byteLength > MAX_COVER_BYTES) return responseError(413, "cover_too_large", "封面不能超过 5 MB。");
        if (!format.valid(bytes)) return responseError(415, "forged_cover", "封面内容与文件类型不一致。");
        await dependencies.assertStorageAdmission(bytes.byteLength, current.bytes);
        const objectKey = `covers/${bookId}/${idFactory()}.${format.extension}`;
        await dependencies.bucket.put(objectKey, bytes, { httpMetadata: { contentType } });
        let result;
        try {
          result = await dependencies.repository.setCover(bookId, {
            objectKey,
            bytes: bytes.byteLength,
            now: now().toISOString(),
          });
        } catch (cause) {
          await dependencies.bucket.delete(objectKey);
          throw cause;
        }
        if (result.previousObjectKey && result.previousObjectKey !== objectKey) {
          await dependencies.bucket.delete(result.previousObjectKey);
        }
        return Response.json({ book: result.book }, { status: 201 });
      } catch (cause) {
        return responseError(400, "cover_upload_failed", cause instanceof Error ? cause.message : "封面上传失败。");
      }
    },

    async delete(bookId: string): Promise<Response> {
      try {
        const result = await dependencies.repository.removeCover(bookId, now().toISOString());
        if (!result) return responseError(404, "book_not_found", "找不到这本书。");
        if (result.previousObjectKey) await dependencies.bucket.delete(result.previousObjectKey);
        return Response.json({ book: result.book });
      } catch (cause) {
        return responseError(400, "cover_delete_failed", cause instanceof Error ? cause.message : "封面移除失败。");
      }
    },
  };
}
