import { promises as fs } from "node:fs";
import { fileURLToPath, pathToFileURL } from "node:url";
import fastGlob from "fast-glob";
import { green } from "kleur/colors";
import micromatch from "micromatch";
import pLimit from "p-limit";
import { getContentEntryIdAndSlug, getEntryConfigByExtMap, posixRelative } from "../utils.js";
function generateIdDefault({ entry, base, data }) {
  if (data.slug) {
    return data.slug;
  }
  const entryURL = new URL(entry, base);
  const { slug } = getContentEntryIdAndSlug({
    entry: entryURL,
    contentDir: base,
    collection: ""
  });
  return slug;
}
function glob(globOptions) {
  if (globOptions.pattern.startsWith("../")) {
    throw new Error(
      "Glob patterns cannot start with `../`. Set the `base` option to a parent directory instead."
    );
  }
  if (globOptions.pattern.startsWith("/")) {
    throw new Error(
      "Glob patterns cannot start with `/`. Set the `base` option to a parent directory or use a relative path instead."
    );
  }
  const generateId = globOptions?.generateId ?? generateIdDefault;
  const fileToIdMap = /* @__PURE__ */ new Map();
  return {
    name: "glob-loader",
    load: async ({ settings, logger, watcher, parseData, store, generateDigest }) => {
      const renderFunctionByContentType = /* @__PURE__ */ new WeakMap();
      const untouchedEntries = new Set(store.keys());
      async function syncData(entry, base, entryType) {
        if (!entryType) {
          logger.warn(`No entry type found for ${entry}`);
          return;
        }
        const fileUrl = new URL(entry, base);
        const contents = await fs.readFile(fileUrl, "utf-8").catch((err) => {
          logger.error(`Error reading ${entry}: ${err.message}`);
          return;
        });
        if (!contents) {
          logger.warn(`No contents found for ${entry}`);
          return;
        }
        const { body, data } = await entryType.getEntryInfo({
          contents,
          fileUrl
        });
        const id = generateId({ entry, base, data });
        untouchedEntries.delete(id);
        const existingEntry = store.get(id);
        const digest = generateDigest(contents);
        if (existingEntry && existingEntry.digest === digest && existingEntry.filePath) {
          if (existingEntry.rendered?.metadata?.imagePaths?.length) {
            store.addAssetImports(
              existingEntry.rendered.metadata.imagePaths,
              existingEntry.filePath
            );
          }
          await parseData(existingEntry);
          return;
        }
        const filePath = fileURLToPath(fileUrl);
        const relativePath = posixRelative(fileURLToPath(settings.config.root), filePath);
        const parsedData = await parseData({
          id,
          data,
          filePath
        });
        if (entryType.getRenderFunction) {
          let render = renderFunctionByContentType.get(entryType);
          if (!render) {
            render = await entryType.getRenderFunction(settings);
            renderFunctionByContentType.set(entryType, render);
          }
          let rendered = void 0;
          try {
            rendered = await render?.({
              id,
              data: parsedData,
              body,
              filePath,
              digest
            });
          } catch (error) {
            logger.error(`Error rendering ${entry}: ${error.message}`);
          }
          store.set({
            id,
            data: parsedData,
            body,
            filePath: relativePath,
            digest,
            rendered
          });
          if (rendered?.metadata?.imagePaths?.length) {
            store.addAssetImports(rendered.metadata.imagePaths, relativePath);
          }
        } else {
          store.set({ id, data: parsedData, body, filePath: relativePath, digest });
        }
        fileToIdMap.set(filePath, id);
      }
      const entryConfigByExt = getEntryConfigByExtMap([
        ...settings.contentEntryTypes,
        ...settings.dataEntryTypes
      ]);
      const baseDir = globOptions.base ? new URL(globOptions.base, settings.config.root) : settings.config.root;
      if (!baseDir.pathname.endsWith("/")) {
        baseDir.pathname = `${baseDir.pathname}/`;
      }
      const files = await fastGlob(globOptions.pattern, {
        cwd: fileURLToPath(baseDir)
      });
      function configForFile(file) {
        const ext = file.split(".").at(-1);
        if (!ext) {
          logger.warn(`No extension found for ${file}`);
          return;
        }
        return entryConfigByExt.get(`.${ext}`);
      }
      const limit = pLimit(10);
      await Promise.all(
        files.map(
          (entry) => limit(async () => {
            const entryType = configForFile(entry);
            await syncData(entry, baseDir, entryType);
          })
        )
      );
      untouchedEntries.forEach((id) => store.delete(id));
      if (!watcher) {
        return;
      }
      const matcher = micromatch.makeRe(globOptions.pattern);
      const matchesGlob = (entry) => !entry.startsWith("../") && matcher.test(entry);
      const basePath = fileURLToPath(baseDir);
      async function onChange(changedPath) {
        const entry = posixRelative(basePath, changedPath);
        if (!matchesGlob(entry)) {
          return;
        }
        const entryType = configForFile(changedPath);
        const baseUrl = pathToFileURL(basePath);
        await syncData(entry, baseUrl, entryType);
        logger.info(`Reloaded data from ${green(entry)}`);
      }
      watcher.on("change", onChange);
      watcher.on("add", onChange);
      watcher.on("unlink", async (deletedPath) => {
        const entry = posixRelative(basePath, deletedPath);
        if (!matchesGlob(entry)) {
          return;
        }
        const id = fileToIdMap.get(deletedPath);
        if (id) {
          store.delete(id);
          fileToIdMap.delete(deletedPath);
        }
      });
    }
  };
}
export {
  glob
};
