import { mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
import path from "node:path";

type PutOptions = { httpMetadata?: { contentType?: string; cacheControl?: string; contentDisposition?: string } };
const root = () => path.resolve(process.env.PLANEXA_STORAGE_PATH || path.join(process.cwd(), "storage"));
const safePath = (key: string) => {
  const normalized = key.replaceAll("\\", "/").replace(/^\/+/, "");
  if (!normalized || normalized.split("/").includes("..")) throw new Error("Invalid storage key");
  const resolved = path.resolve(root(), normalized);
  if (!resolved.startsWith(`${root()}${path.sep}`)) throw new Error("Invalid storage key");
  return resolved;
};
async function toBytes(value: string | ArrayBuffer | ArrayBufferView | Blob | ReadableStream) {
  if (typeof value === "string") return Buffer.from(value);
  if (value instanceof ArrayBuffer) return Buffer.from(value);
  if (ArrayBuffer.isView(value)) return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
  return Buffer.from(await new Response(value as BodyInit).arrayBuffer());
}

export const localFiles = {
  async get(key: string) {
    try {
      const filePath = safePath(key);
      const body = await readFile(filePath);
      let httpMetadata: PutOptions["httpMetadata"] = {};
      try { httpMetadata = JSON.parse(await readFile(`${filePath}.meta.json`, "utf8")); } catch { /* optional */ }
      return { body, httpMetadata, async json<T>() { return JSON.parse(body.toString("utf8")) as T; } };
    } catch (error) {
      if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
      throw error;
    }
  },
  async put(key: string, value: string | ArrayBuffer | ArrayBufferView | Blob | ReadableStream, options: PutOptions = {}) {
    const filePath = safePath(key);
    await mkdir(path.dirname(filePath), { recursive: true });
    const temp = `${filePath}.${crypto.randomUUID()}.tmp`;
    await writeFile(temp, await toBytes(value));
    await rename(temp, filePath);
    if (options.httpMetadata) await writeFile(`${filePath}.meta.json`, JSON.stringify(options.httpMetadata));
  },
  async delete(key: string) {
    const filePath = safePath(key);
    await Promise.all([rm(filePath, { force: true }), rm(`${filePath}.meta.json`, { force: true })]);
  },
  async list({ prefix, limit = 100 }: { prefix: string; limit?: number }) {
    const directory = safePath(prefix.endsWith("/") ? `${prefix}placeholder` : `${prefix}/placeholder`);
    const base = path.dirname(directory);
    try {
      const names = await readdir(base);
      return { objects: names.filter((name) => !name.endsWith(".meta.json")).slice(0, limit).map((name) => ({ key: `${prefix.replace(/\/?$/, "/")}${name}` })) };
    } catch (error) {
      if ((error as NodeJS.ErrnoException).code === "ENOENT") return { objects: [] };
      throw error;
    }
  },
};
