import { prefixStorage } from 'unstorage';
import { hash } from 'ohash';
import destr from 'destr';
import { p as parse, t as transform } from './index.mjs';
import { a as useStorage, u as useRuntimeConfig } from './nitro/node-server.mjs';

const get = (obj, path) => path.split(".").reduce((acc, part) => acc && acc[part], obj);
const _pick = (obj, condition) => Object.keys(obj).filter(condition).reduce((newObj, key) => Object.assign(newObj, { [key]: obj[key] }), {});
const pick = (keys) => (obj) => keys && keys.length ? _pick(obj, (key) => keys.includes(key)) : obj;
const omit = (keys) => (obj) => keys && keys.length ? _pick(obj, (key) => !keys.includes(key)) : obj;
const apply = (fn) => (data) => Array.isArray(data) ? data.map((item) => fn(item)) : fn(data);
const sortComparable = (key, direction) => (a, b) => {
  const values = [get(a, key), get(b, key)];
  if (direction === "desc" || direction === true) {
    values.reverse();
  }
  if (values[0] === void 0) {
    return 1;
  }
  if (values[1] === void 0) {
    return -1;
  }
  if (values[0] > values[1]) {
    return 1;
  }
  if (values[0] < values[1]) {
    return -1;
  }
  return 0;
};
const sortByKey = (data, key, direction) => data.sort(sortComparable(key, direction));
const assertArray = (value, message = "Expected an array") => {
  if (!Array.isArray(value)) {
    throw new TypeError(message);
  }
};
const ensureArray = (value) => Array.isArray(value) ? value : [value];

const createQuery = (fetcher, queryParams) => {
  const params = {
    sortBy: [],
    ...queryParams,
    where: queryParams?.where ? ensureArray(queryParams.where) : [],
    only: queryParams?.only ? ensureArray(queryParams.only) : [],
    without: queryParams?.without ? ensureArray(queryParams.without) : []
  };
  const $set = (key, fn = (v) => v) => {
    return (...values) => {
      params[key] = fn(...values);
      return query;
    };
  };
  const query = {
    params: () => Object.freeze(params),
    only: $set("only", ensureArray),
    without: $set("without", ensureArray),
    where: $set("where", (q) => [...params.where, q]),
    sortBy: $set("sortBy", (field, direction) => [...params.sortBy, [field, direction]]),
    limit: $set("limit", (v) => parseInt(String(v), 10)),
    skip: $set("skip", (v) => parseInt(String(v), 10)),
    findOne: () => fetcher({ ...params, first: true }),
    find: () => fetcher(params),
    findSurround: (query2, options) => fetcher({ ...params, surround: { query: query2, ...options } }),
    locale: (locale) => query.where({ locale })
  };
  return query;
};

function createMatch(opts = {}) {
  const operators = createOperators(match, opts.operators);
  function match(item, conditions) {
    if (typeof conditions !== "object" || conditions instanceof RegExp) {
      return operators.$eq(item, conditions);
    }
    return Object.keys(conditions || {}).every((key) => {
      const condition = conditions[key];
      if (key.startsWith("$")) {
        const fn = operators[key];
        return typeof fn === "function" ? fn(item, condition) : false;
      }
      return match(get(item, key), condition);
    });
  }
  return match;
}
function createOperators(match, operators = {}) {
  return {
    $match: (item, condition) => match(item, condition),
    $eq: (item, condition) => condition instanceof RegExp ? condition.test(item) : item === condition,
    $ne: (item, condition) => condition instanceof RegExp ? !condition.test(item) : item !== condition,
    $not: (item, condition) => !match(item, condition),
    $and: (item, condition) => {
      assertArray(condition, "$and requires an array as condition");
      return condition.every((cond) => match(item, cond));
    },
    $or: (item, condition) => {
      assertArray(condition, "$or requires an array as condition");
      return condition.some((cond) => match(item, cond));
    },
    $in: (item, condition) => ensureArray(condition).some((cond) => match(item, cond)),
    $contains: (item, condition) => {
      item = Array.isArray(item) ? item : String(item);
      return ensureArray(condition).every((i) => item.includes(i));
    },
    $icontains: (item, condition) => {
      if (typeof condition !== "string") {
        throw new TypeError("$icontains requires a string, use $contains instead");
      }
      item = String(item).toLocaleLowerCase();
      return ensureArray(condition).every((i) => item.includes(i.toLocaleLowerCase()));
    },
    $containsAny: (item, condition) => {
      assertArray(condition, "$containsAny requires an array as condition");
      item = Array.isArray(item) ? item : String(item);
      return condition.some((i) => item.includes(i));
    },
    $exists: (item, condition) => condition ? typeof item !== "undefined" : typeof item === "undefined",
    $type: (item, condition) => typeof item === String(condition),
    $regex: (item, condition) => {
      if (!(condition instanceof RegExp)) {
        const matched = String(condition).match(/\/(.*)\/([dgimsuy]*)$/);
        condition = matched ? new RegExp(matched[1], matched[2] || "") : new RegExp(condition);
      }
      return condition.test(String(item || ""));
    },
    ...operators || {}
  };
}

function createPipelineFetcher(getContentsList) {
  const match = createMatch();
  const surround = (data, { query, before, after }) => {
    const matchQuery = typeof query === "string" ? { slug: query } : query;
    const index = data.findIndex((item) => match(item, matchQuery));
    before = before || 1;
    after = after || 1;
    const slice = new Array(before + after).fill(null, 0);
    return index === -1 ? slice : slice.map((_, i) => data[index - before + i + Number(i >= before)] || null);
  };
  const pipelines = [
    (data, params) => params.slug ? data.filter((item) => String(item.slug).startsWith(params.slug)) : data,
    (data, params) => data.filter((item) => ensureArray(params.where).every((matchQuery) => match(item, matchQuery))),
    (data, params) => ensureArray(params.sortBy).forEach(([key, direction]) => sortByKey(data, key, direction)),
    (data, params) => params.surround ? surround(data, params.surround) : data,
    (data, params) => params.skip ? data.slice(params.skip) : data,
    (data, params) => params.limit ? data.slice(0, params.limit) : data,
    (data, params) => apply(omit(params.without))(data),
    (data, params) => apply(pick(params.only))(data),
    (data, params) => params.first ? data[0] : data
  ];
  return async (params) => {
    const data = await getContentsList();
    if (!params.sortBy || !params.sortBy.length) {
      params.sortBy = [
        ["path", "asc"]
      ];
    }
    return pipelines.reduce(($data, pipe) => pipe($data, params) || $data, data);
  };
}

const sourceStorage = prefixStorage(useStorage(), "content:source");
prefixStorage(useStorage(), "cache:content");
const cacheParsedStorage = prefixStorage(useStorage(), "cache:content:parsed");
const contentIgnores = useRuntimeConfig().content.ignores.map((p) => typeof p === "string" ? new RegExp(`^${p}`) : p);
const contentIgnorePredicate = (key) => !contentIgnores.some((prefix) => key.split(":").some((k) => prefix.test(k)));
const getContentsIds = async (prefix) => {
  let keys = [];
  {
    keys = await cacheParsedStorage.getKeys(prefix);
  }
  if (keys.length === 0) {
    keys = await sourceStorage.getKeys(prefix);
  }
  return keys.filter(contentIgnorePredicate);
};
const getContentsList = async (prefix) => {
  const keys = await getContentsIds(prefix);
  const contents = await Promise.all(keys.map((key) => getContent(key)));
  return contents;
};
const getContent = async (id) => {
  if (!contentIgnorePredicate(id)) {
    return { id, body: null };
  }
  const cached = await cacheParsedStorage.getItem(id);
  if (cached) {
    return cached.parsed;
  }
  const meta = await sourceStorage.getMeta(id);
  const hash$1 = hash(meta);
  if (cached?.hash === hash$1) {
    return cached.parsed;
  }
  const body = await sourceStorage.getItem(id);
  if (body === null) {
    return { id, body: null };
  }
  const parsedContent = await parse(id, body).then(transform);
  const parsed = {
    ...meta,
    ...parsedContent
  };
  await cacheParsedStorage.setItem(id, { parsed, hash: hash$1 }).catch(() => {
  });
  return parsed;
};
const queryContent = (body, params) => {
  if (typeof body === "string") {
    body = {
      slug: body,
      ...params
    };
  }
  const pipelineFetcher = createPipelineFetcher(getContentsList);
  return createQuery(pipelineFetcher, body);
};
const _queries = {};
const useApiQuery = (qid, query) => {
  if (query) {
    _queries[qid] = typeof query === "string" ? destr(query) : query;
  }
  return _queries[qid] || {};
};

export { queryContent as q, useApiQuery as u };
//# sourceMappingURL=storage.mjs.map
