import { del, head, list, put } from "@vercel/blob";
import { defineDriver, normalizeKey, joinKeys, createError } from "./utils/index.mjs";
const DRIVER_NAME = "vercel-blob";
export default defineDriver((opts) => {
  const optsBase = normalizeKey(opts?.base);
  const r = (...keys) => joinKeys(optsBase, ...keys).replace(/:/g, "/");
  const envName = `${opts.envPrefix || "BLOB"}_READ_WRITE_TOKEN`;
  const getToken = () => {
    if (opts.access !== "public") {
      throw createError(DRIVER_NAME, `You must set { access: "public" }`);
    }
    const token = opts.token || globalThis.process?.env?.[envName];
    if (!token) {
      throw createError(
        DRIVER_NAME,
        `Missing token. Set ${envName} env or token config.`
      );
    }
    return token;
  };
  const get = async (key) => {
    const { blobs } = await list({
      token: getToken(),
      prefix: r(key)
    });
    const blob = blobs.find((item) => item.pathname === r(key));
    return blob;
  };
  return {
    name: DRIVER_NAME,
    options: opts,
    async hasItem(key) {
      const blob = await get(key);
      return !!blob;
    },
    async getItem(key) {
      const blob = await get(key);
      return blob ? fetch(blob.url).then((res) => res.text()) : null;
    },
    async getItemRaw(key) {
      const blob = await get(key);
      return blob ? fetch(blob.url).then((res) => res.arrayBuffer()) : null;
    },
    async getMeta(key) {
      const blob = await get(key);
      if (!blob) return null;
      const blobHead = await head(blob.url, {
        token: getToken()
      });
      if (!blobHead) return null;
      return {
        mtime: blobHead.uploadedAt,
        ...blobHead
      };
    },
    async setItem(key, value, opts2) {
      await put(r(key), value, {
        access: "public",
        addRandomSuffix: false,
        token: getToken(),
        ...opts2
      });
    },
    async setItemRaw(key, value, opts2) {
      await put(r(key), value, {
        access: "public",
        addRandomSuffix: false,
        token: getToken(),
        ...opts2
      });
    },
    async removeItem(key) {
      const blob = await get(key);
      if (blob) await del(blob.url, { token: getToken() });
    },
    async getKeys(base) {
      const blobs = [];
      let cursor = void 0;
      do {
        const listBlobResult = await list({
          token: getToken(),
          cursor,
          prefix: r(base)
        });
        cursor = listBlobResult.cursor;
        for (const blob of listBlobResult.blobs) {
          blobs.push(blob);
        }
      } while (cursor);
      return blobs.map(
        (blob) => blob.pathname.replace(
          new RegExp(`^${optsBase.replace(/:/g, "/")}/`),
          ""
        )
      );
    },
    async clear(base) {
      let cursor = void 0;
      const blobs = [];
      do {
        const listBlobResult = await list({
          token: getToken(),
          cursor,
          prefix: r(base)
        });
        blobs.push(...listBlobResult.blobs);
        cursor = listBlobResult.cursor;
      } while (cursor);
      if (blobs.length > 0) {
        await del(
          blobs.map((blob) => blob.url),
          {
            token: getToken()
          }
        );
      }
    }
  };
});
