const crypto = require("crypto");
const fs = require("fs");
const path = require("path");

class ToolCache {
  constructor(options = {}) {
    this.directory = options.directory;
    this.now = options.now || Date.now;
  }

  get(key, ttlSeconds) {
    if (!ttlSeconds) return null;
    const file = this.fileFor(key);
    try {
      const value = JSON.parse(fs.readFileSync(file, "utf8"));
      if (!Number.isFinite(value.created_at) || value.created_at + ttlSeconds * 1000 <= this.now()) {
        fs.rmSync(file, { force: true });
        return null;
      }
      return value.data;
    } catch {
      return null;
    }
  }

  set(key, data) {
    fs.mkdirSync(this.directory, { recursive: true });
    const file = this.fileFor(key);
    const temporary = `${file}.${process.pid}.tmp`;
    fs.writeFileSync(temporary, `${JSON.stringify({ created_at: this.now(), data })}\n`);
    try { fs.renameSync(temporary, file); }
    catch (error) {
      if (!["EEXIST", "EPERM"].includes(error.code)) throw error;
      fs.rmSync(file, { force: true });
      fs.renameSync(temporary, file);
    }
    this.prune(200);
    return data;
  }

  clear() {
    fs.rmSync(this.directory, { recursive: true, force: true });
    fs.mkdirSync(this.directory, { recursive: true });
  }

  stats() {
    try {
      const files = fs.readdirSync(this.directory).filter((name) => name.endsWith(".json"));
      return {
        entries: files.length,
        bytes: files.reduce((total, name) => total + fs.statSync(path.join(this.directory, name)).size, 0)
      };
    } catch {
      return { entries: 0, bytes: 0 };
    }
  }

  prune(maximum) {
    let files;
    try {
      files = fs.readdirSync(this.directory)
        .filter((name) => name.endsWith(".json"))
        .map((name) => ({ name, mtime: fs.statSync(path.join(this.directory, name)).mtimeMs }))
        .sort((left, right) => right.mtime - left.mtime);
    } catch {
      return;
    }
    for (const file of files.slice(maximum)) fs.rmSync(path.join(this.directory, file.name), { force: true });
  }

  fileFor(key) {
    return path.join(this.directory, `${crypto.createHash("sha256").update(String(key)).digest("hex")}.json`);
  }
}

module.exports = { ToolCache };
