// src/page-versioning.ts
import { createHash } from "crypto";
import path from "path";
import {
  access,
  mkdir,
  readFile,
  writeFile,
  unlink
} from "fs/promises";
var NOOP_LOGGER = {
  debug: () => {
  },
  warn: () => {
  }
};
var writeLocks = /* @__PURE__ */ new Map();
function withPageLock(pageKey, fn) {
  const prev = writeLocks.get(pageKey) ?? Promise.resolve();
  const next = prev.then(fn, fn);
  writeLocks.set(pageKey, next.then(() => {
  }, () => {
  }));
  return next;
}
function contentHash(content) {
  return createHash("sha256").update(content, "utf-8").digest("hex");
}
function sidecarKey(pagePath) {
  const withoutExt = pagePath.replace(/\.md$/i, "");
  return withoutExt.replace(/[\\/]/g, "__");
}
function sidecarDir(memoryDir, sidecar, pagePath) {
  return path.join(memoryDir, sidecar, sidecarKey(pagePath));
}
function manifestPath(memoryDir, sidecar, pagePath) {
  return path.join(sidecarDir(memoryDir, sidecar, pagePath), "manifest.json");
}
async function fileExists(p) {
  try {
    await access(p);
    return true;
  } catch {
    return false;
  }
}
async function readManifest(memoryDir, sidecar, pagePath) {
  const mp = manifestPath(memoryDir, sidecar, pagePath);
  try {
    const raw = await readFile(mp, "utf-8");
    const parsed = JSON.parse(raw);
    if (typeof parsed !== "object" || parsed === null) {
      return { pagePath, versions: [], currentVersion: "0" };
    }
    const obj = parsed;
    const versions = Array.isArray(obj.versions) ? obj.versions : [];
    const currentVersion = typeof obj.currentVersion === "string" ? obj.currentVersion : "0";
    return { pagePath: typeof obj.pagePath === "string" ? obj.pagePath : pagePath, versions, currentVersion };
  } catch {
    return { pagePath, versions: [], currentVersion: "0" };
  }
}
async function writeManifest(memoryDir, sidecar, pagePath, history) {
  const dir = sidecarDir(memoryDir, sidecar, pagePath);
  await mkdir(dir, { recursive: true });
  const mp = manifestPath(memoryDir, sidecar, pagePath);
  await writeFile(mp, JSON.stringify(history, null, 2) + "\n", "utf-8");
}
async function createVersion(pagePath, content, trigger, config, log = NOOP_LOGGER, note, memoryDir) {
  const { sidecarDir: sidecar, maxVersionsPerPage } = config;
  const resolvedMemoryDir = memoryDir ?? resolveMemoryDir(pagePath);
  const mPath = manifestPath(resolvedMemoryDir, sidecar, relPath(pagePath, resolvedMemoryDir));
  return withPageLock(mPath, async () => {
    const history = await readManifest(resolvedMemoryDir, sidecar, relPath(pagePath, resolvedMemoryDir));
    const nextId = String(history.versions.length > 0 ? Math.max(...history.versions.map((v) => Number(v.versionId))) + 1 : 1);
    const hash = contentHash(content);
    const version = {
      versionId: nextId,
      timestamp: (/* @__PURE__ */ new Date()).toISOString(),
      contentHash: hash,
      sizeBytes: Buffer.byteLength(content, "utf-8"),
      trigger,
      ...note !== void 0 ? { note } : {}
    };
    const dir = sidecarDir(resolvedMemoryDir, sidecar, relPath(pagePath, resolvedMemoryDir));
    await mkdir(dir, { recursive: true });
    const ext = path.extname(pagePath) || ".md";
    const snapshotPath = path.join(dir, `${nextId}${ext}`);
    await writeFile(snapshotPath, content, "utf-8");
    history.versions.push(version);
    history.currentVersion = nextId;
    if (maxVersionsPerPage > 0 && history.versions.length > maxVersionsPerPage) {
      const toRemove = history.versions.splice(0, history.versions.length - maxVersionsPerPage);
      for (const old of toRemove) {
        const oldPath = path.join(dir, `${old.versionId}${ext}`);
        try {
          await unlink(oldPath);
        } catch {
          log.debug(`page-versioning: could not remove old snapshot ${oldPath}`);
        }
      }
    }
    await writeManifest(resolvedMemoryDir, sidecar, relPath(pagePath, resolvedMemoryDir), history);
    log.debug(`page-versioning: created version ${nextId} for ${pagePath} (trigger=${trigger})`);
    return version;
  });
}
async function listVersions(pagePath, config, memoryDir) {
  const resolvedMemoryDir = memoryDir ?? resolveMemoryDir(pagePath);
  const rel = relPath(pagePath, resolvedMemoryDir);
  const history = await readManifest(resolvedMemoryDir, config.sidecarDir, rel);
  history.versions.sort((a, b) => Number(a.versionId) - Number(b.versionId));
  return history;
}
async function getVersion(pagePath, versionId, config, memoryDir) {
  const resolvedMemoryDir = memoryDir ?? resolveMemoryDir(pagePath);
  const rel = relPath(pagePath, resolvedMemoryDir);
  const ext = path.extname(pagePath) || ".md";
  const dir = sidecarDir(resolvedMemoryDir, config.sidecarDir, rel);
  const snapshotPath = path.join(dir, `${versionId}${ext}`);
  if (!await fileExists(snapshotPath)) {
    throw new Error(`Version ${versionId} not found for ${pagePath}`);
  }
  return readFile(snapshotPath, "utf-8");
}
async function revertToVersion(pagePath, versionId, config, log = NOOP_LOGGER, memoryDir) {
  const resolvedMemoryDir = memoryDir ?? resolveMemoryDir(pagePath);
  const targetContent = await getVersion(pagePath, versionId, config, resolvedMemoryDir);
  let currentContent = "";
  try {
    currentContent = await readFile(pagePath, "utf-8");
  } catch {
  }
  const version = await createVersion(
    pagePath,
    currentContent,
    "revert",
    config,
    log,
    `reverted to version ${versionId}`,
    resolvedMemoryDir
  );
  await writeFile(pagePath, targetContent, "utf-8");
  log.debug(`page-versioning: reverted ${pagePath} to version ${versionId}`);
  return version;
}
async function diffVersions(pagePath, v1, v2, config, memoryDir) {
  const resolvedMemoryDir = memoryDir ?? resolveMemoryDir(pagePath);
  const content1 = await getVersion(pagePath, v1, config, resolvedMemoryDir);
  const content2 = await getVersion(pagePath, v2, config, resolvedMemoryDir);
  const lines1 = content1.split("\n");
  const lines2 = content2.split("\n");
  const result = [];
  result.push(`--- version ${v1}`);
  result.push(`+++ version ${v2}`);
  const lcs = computeLCS(lines1, lines2);
  let i = 0;
  let j = 0;
  let k = 0;
  while (k < lcs.length) {
    while (i < lines1.length && lines1[i] !== lcs[k]) {
      result.push(`-${lines1[i]}`);
      i++;
    }
    while (j < lines2.length && lines2[j] !== lcs[k]) {
      result.push(`+${lines2[j]}`);
      j++;
    }
    result.push(` ${lcs[k]}`);
    i++;
    j++;
    k++;
  }
  while (i < lines1.length) {
    result.push(`-${lines1[i]}`);
    i++;
  }
  while (j < lines2.length) {
    result.push(`+${lines2[j]}`);
    j++;
  }
  return result.join("\n");
}
function computeLCS(a, b) {
  const m = a.length;
  const n = b.length;
  const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
  for (let i2 = 1; i2 <= m; i2++) {
    for (let j2 = 1; j2 <= n; j2++) {
      if (a[i2 - 1] === b[j2 - 1]) {
        dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
      } else {
        dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
      }
    }
  }
  const result = [];
  let i = m;
  let j = n;
  while (i > 0 && j > 0) {
    if (a[i - 1] === b[j - 1]) {
      result.unshift(a[i - 1]);
      i--;
      j--;
    } else if (dp[i - 1][j] > dp[i][j - 1]) {
      i--;
    } else {
      j--;
    }
  }
  return result;
}
function resolveMemoryDir(pagePath) {
  const knownSubdirs = /* @__PURE__ */ new Set([
    "facts",
    "corrections",
    "entities",
    "state",
    "artifacts",
    "questions",
    "profiles"
  ]);
  let dir = path.dirname(pagePath);
  for (let depth = 0; depth < 5; depth++) {
    const base = path.basename(dir);
    if (knownSubdirs.has(base) || /^\d{4}-\d{2}-\d{2}$/.test(base)) {
      dir = path.dirname(dir);
    } else {
      break;
    }
  }
  return dir;
}
function relPath(pagePath, memoryDir) {
  return path.relative(memoryDir, pagePath);
}

export {
  sidecarKey,
  createVersion,
  listVersions,
  getVersion,
  revertToVersion,
  diffVersions
};
//# sourceMappingURL=chunk-FAAFWE4G.js.map