import os from "os";
import path from "path";
import * as C from "cachified";
import {
  lruCacheAdapter,
  verboseReporter
} from "cachified";
import fsExtra from "fs-extra";
import { LRUCache } from "lru-cache";
import md5 from "md5-hex";
import { singleton } from "./singleton.server.js";
import { cachifiedTimingReporter } from "./timing.server.js";
const solutionAppCache = makeSingletonCache("SolutionAppCache");
const problemAppCache = makeSingletonCache("ProblemAppCache");
const exampleAppCache = makeSingletonCache("ExampleAppCache");
const playgroundAppCache = makeSingletonCache("PlaygroundAppCache");
const appsCache = makeSingletonCache("AppsCache");
const diffCodeCache = makeSingletonCache("DiffCodeCache");
const diffFilesCache = makeSingletonCache("DiffFilesCache");
const compiledMarkdownCache = makeSingletonCache(
  "CompiledMarkdownCache"
);
const embeddedFilesCache = makeSingletonCache("EmbeddedFilesCache");
const presenceCache = makeSingletonCache("PresenceCache");
const cacheDir = path.join(os.homedir(), ".kcdshop", "cache");
const fsCache = {
  name: "Filesystem cache",
  async get(key) {
    try {
      const filePath = path.join(cacheDir, md5(key));
      const data = await fsExtra.readJSON(filePath);
      return data;
    } catch (error) {
      if (error instanceof Error && "code" in error && error.code === "ENOENT") {
        return null;
      }
      throw error;
    }
  },
  async set(key, entry) {
    const filePath = path.join(cacheDir, md5(key));
    await fsExtra.ensureDir(path.dirname(filePath));
    await fsExtra.writeJSON(filePath, entry);
  },
  async delete(key) {
    const filePath = path.join(cacheDir, md5(key));
    await fsExtra.remove(filePath);
  }
};
async function deleteCache() {
  if (process.env.KCDSHOP_DEPLOYED)
    return null;
  try {
    if (await fsExtra.exists(cacheDir)) {
      await fsExtra.remove(cacheDir);
    }
  } catch (error) {
    console.error(`Error deleting the cache in ${cacheDir}`, error);
  }
}
function makeSingletonCache(name) {
  return singleton(name, () => {
    const cache = new LRUCache({
      max: 1e3
    });
    cache.name = name;
    return lruCacheAdapter(cache);
  });
}
async function cachified({
  request,
  timings,
  key,
  timingKey = key.length > 18 ? key.slice(0, 7) + "..." + key.slice(-8) : key,
  ...options
}) {
  return C.cachified({
    ...options,
    key,
    forceFresh: await shouldForceFresh({
      forceFresh: options.forceFresh,
      request,
      key
    }),
    reporter: C.mergeReporters(
      cachifiedTimingReporter(timings),
      options.reporter,
      process.env.KCDSHOP_DEBUG_CACHE ? verboseReporter() : void 0
    )
  });
}
async function shouldForceFresh({
  forceFresh,
  request,
  key
}) {
  if (typeof forceFresh === "boolean")
    return forceFresh;
  if (typeof forceFresh === "string" && key) {
    return forceFresh.split(",").includes(key);
  }
  if (!request)
    return false;
  const fresh = new URL(request.url).searchParams.get("fresh");
  if (typeof fresh !== "string")
    return false;
  if (fresh === "")
    return true;
  if (!key)
    return false;
  return fresh.split(",").includes(key);
}
export {
  appsCache,
  cachified,
  compiledMarkdownCache,
  deleteCache,
  diffCodeCache,
  diffFilesCache,
  embeddedFilesCache,
  exampleAppCache,
  fsCache,
  playgroundAppCache,
  presenceCache,
  problemAppCache,
  shouldForceFresh,
  solutionAppCache
};
