import { readFile } from "node:fs/promises";
import { parseDocument } from "yaml";

export type CatalogIndex<T> = {
  description: string;
  metadata: T;
};

export function parseCatalogIndex<T>(source: string, indexPath: string): CatalogIndex<T> {
  const lines = source.split(/\r?\n/);
  if (lines[0] !== "---") {
    throw new Error(`${indexPath}: YAML Front Matter must start with ---`);
  }

  const closingDelimiter = lines.indexOf("---", 1);
  if (closingDelimiter === -1) {
    throw new Error(`${indexPath}: YAML Front Matter is missing its closing ---`);
  }

  const document = parseDocument(lines.slice(1, closingDelimiter).join("\n"), {
    prettyErrors: true,
  });
  if (document.errors.length > 0) {
    throw new Error(
      `${indexPath}: ${document.errors.map((error) => error.message).join("; ")}`,
    );
  }

  const metadata = document.toJS() as unknown;
  if (typeof metadata !== "object" || metadata === null || Array.isArray(metadata)) {
    throw new Error(`${indexPath}: YAML Front Matter must contain an object`);
  }
  if (Object.hasOwn(metadata, "description")) {
    throw new Error(`${indexPath}: description must be the Markdown body`);
  }

  const description = lines.slice(closingDelimiter + 1).join("\n").trim();
  if (description.length === 0) {
    throw new Error(`${indexPath}: Markdown description is missing`);
  }

  return { metadata: metadata as T, description };
}

export async function readCatalogIndex<T>(indexPath: string): Promise<CatalogIndex<T>> {
  return parseCatalogIndex<T>(await readFile(indexPath, "utf8"), indexPath);
}
