import {
  createDocument,
  getDocument,
  getDocuments,
  transformDocument,
} from "@lib/documents.ts";
import { Root } from "https://esm.sh/remark-frontmatter@4.0.1";
import { getThumbhash } from "@lib/cache/image.ts";

type Resource = {
  name: string;
  id: string;
  meta: {
    image?: string;
    author?: string;
    thumbnail?: string;
  };
};

export async function addThumbnailToResource<T = Resource>(
  res: T,
): Promise<T> {
  const imageUrl = res?.meta?.image;
  if (!imageUrl) return res;
  const [thumbhash, average] = await getThumbhash({ url: imageUrl });
  if (!thumbhash) return res;
  return {
    ...res,
    meta: {
      ...res?.meta,
      average: average,
      thumbnail: thumbhash,
    },
  };
}

export function createCrud<T>(
  { prefix, parse, render, hasThumbnails = false }: {
    prefix: string;
    hasThumbnails?: boolean;
    render?: (doc: T) => string;
    parse: (doc: string, id: string) => T;
  },
) {
  function pathFromId(id: string) {
    return `${prefix}${id.replaceAll(":", "")}.md`;
  }

  async function read(id: string) {
    const path = pathFromId(id);
    const content = await getDocument(path);

    const res = parse(content, id);

    if (hasThumbnails) {
      return addThumbnailToResource(res);
    }

    return res;
  }
  function create(id: string, content: string | ArrayBuffer | T) {
    const path = pathFromId(id);
    if (
      typeof content === "string" || content instanceof ArrayBuffer
    ) {
      return createDocument(path, content);
    }

    if (render) {
      return createDocument(path, render(content));
    }

    throw new Error("No renderer defined for " + prefix + " CRUD");
  }

  async function update(id: string, updater: (r: Root) => Root) {
    const path = pathFromId(id);
    const content = await getDocument(path);
    const newDoc = transformDocument(content, updater);
    await createDocument(path, newDoc);
  }

  async function readAll() {
    const allDocuments = await getDocuments();
    return Promise.all(
      allDocuments.filter((d) => {
        return d.name.startsWith(prefix) &&
          d.contentType === "text/markdown" &&
          !d.name.endsWith("index.md");
      }).map((doc) => {
        const id = doc.name.replace(prefix, "").replace(/\.md$/, "");
        return read(id);
      }),
    );
  }

  return {
    read,
    readAll,
    create,
    update,
  };
}
