import {
  isSafeRouteNamespace
} from "./chunk-2LGMW3DJ.js";
import {
  graphFilePath,
  graphsDir,
  readEdgesStrict,
  withGraphWriteLock
} from "./chunk-RK2Y4XOM.js";
import {
  DEFAULT_DECAY_FLOOR,
  DEFAULT_DECAY_PER_WINDOW,
  DEFAULT_DECAY_WINDOW_MS,
  decayEdgeConfidence,
  readEdgeConfidence
} from "./chunk-2LSZVONP.js";
import "./chunk-WIICJPET.js";
import "./chunk-PZ5AY32C.js";

// src/maintenance/graph-edge-decay.ts
import {
  mkdir,
  readdir,
  readFile,
  rename,
  writeFile
} from "fs/promises";
import path from "path";
var DEFAULT_VISIBILITY_THRESHOLD = 0.2;
var GRAPH_TYPES = ["entity", "time", "causal"];
function graphEdgeDecayStatusPath(memoryDir) {
  return path.join(memoryDir, "state", "graph-edge-decay-status.json");
}
async function writeJsonlAtomic(filePath, edges) {
  await mkdir(path.dirname(filePath), { recursive: true });
  const body = edges.length === 0 ? "" : edges.map((e) => JSON.stringify(e)).join("\n") + "\n";
  const tempPath = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
  await writeFile(tempPath, body, "utf-8");
  await rename(tempPath, filePath);
}
async function runGraphEdgeDecayMaintenance(memoryDir, options = {}) {
  const startedAt = Date.now();
  const ranAt = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
  const windowMs = options.windowMs ?? DEFAULT_DECAY_WINDOW_MS;
  const perWindow = options.perWindow ?? DEFAULT_DECAY_PER_WINDOW;
  const floor = options.floor ?? DEFAULT_DECAY_FLOOR;
  const visibilityThreshold = typeof options.visibilityThreshold === "number" && Number.isFinite(options.visibilityThreshold) ? Math.max(0, Math.min(1, options.visibilityThreshold)) : DEFAULT_VISIBILITY_THRESHOLD;
  const dryRun = options.dryRun === true;
  await mkdir(graphsDir(memoryDir), { recursive: true });
  const perType = [];
  const dropByLabel = /* @__PURE__ */ new Map();
  let edgesTotal = 0;
  let edgesDecayed = 0;
  let edgesBelowVisibilityThreshold = 0;
  for (const type of GRAPH_TYPES) {
    const filePath = graphFilePath(memoryDir, type);
    const {
      typeDecayed,
      typeBelow,
      typeTotal
    } = await withGraphWriteLock(filePath, async () => {
      const edges = await readEdgesStrict(memoryDir, type);
      const updated = new Array(edges.length);
      let localDecayed = 0;
      let localBelow = 0;
      let localChangedAny = false;
      for (let i = 0; i < edges.length; i += 1) {
        const edge = edges[i];
        const before = readEdgeConfidence(edge);
        const decayed = decayEdgeConfidence(edge, ranAt, { windowMs, perWindow, floor });
        const after = readEdgeConfidence(decayed);
        if (after < before) {
          localDecayed += 1;
          const drop = before - after;
          const label = typeof edge.label === "string" ? edge.label : "";
          if (label.length > 0) {
            const prev = dropByLabel.get(label);
            if (prev) {
              prev.totalDrop += drop;
              prev.edgeCount += 1;
            } else {
              dropByLabel.set(label, { totalDrop: drop, edgeCount: 1 });
            }
          }
        }
        if (after < visibilityThreshold) {
          localBelow += 1;
        }
        updated[i] = decayed;
        if (decayed.confidence !== edge.confidence || decayed.lastReinforcedAt !== edge.lastReinforcedAt) {
          localChangedAny = true;
        }
      }
      if (!dryRun && localChangedAny && edges.length > 0) {
        await writeJsonlAtomic(filePath, updated);
      }
      return {
        typeDecayed: localDecayed,
        typeBelow: localBelow,
        typeTotal: edges.length
      };
    });
    edgesDecayed += typeDecayed;
    edgesBelowVisibilityThreshold += typeBelow;
    edgesTotal += typeTotal;
    perType.push({
      type,
      edgesTotal: typeTotal,
      edgesDecayed: typeDecayed,
      edgesBelowVisibilityThreshold: typeBelow
    });
  }
  const topDecayedEntities = [...dropByLabel.entries()].map(([label, agg]) => ({ label, totalDrop: agg.totalDrop, edgeCount: agg.edgeCount })).sort((a, b) => {
    if (b.totalDrop !== a.totalDrop) return b.totalDrop - a.totalDrop;
    return a.label.localeCompare(b.label);
  }).slice(0, 5);
  const telemetry = {
    ranAt,
    durationMs: Date.now() - startedAt,
    edgesTotal,
    edgesDecayed,
    edgesBelowVisibilityThreshold,
    topDecayedEntities,
    perType,
    windowMs,
    perWindow,
    floor,
    visibilityThreshold
  };
  if (!dryRun) {
    await persistTelemetry(memoryDir, telemetry);
  }
  return telemetry;
}
async function persistTelemetry(memoryDir, telemetry) {
  const statusPath = graphEdgeDecayStatusPath(memoryDir);
  await mkdir(path.dirname(statusPath), { recursive: true });
  const tempPath = `${statusPath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
  try {
    await writeFile(tempPath, JSON.stringify(telemetry, null, 2) + "\n", "utf-8");
    await rename(tempPath, statusPath);
  } catch {
  }
}
async function readGraphEdgeDecayStatus(memoryDir) {
  try {
    const raw = await readFile(graphEdgeDecayStatusPath(memoryDir), "utf-8");
    const parsed = JSON.parse(raw);
    if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
      return null;
    }
    return parsed;
  } catch {
    return null;
  }
}
async function discoverGraphNamespaceRoots(memoryDir, options) {
  const seen = /* @__PURE__ */ new Map();
  seen.set(options.defaultNamespace, memoryDir);
  if (!options.namespacesEnabled) {
    return [...seen.entries()].map(([namespace, storageRoot]) => ({ namespace, storageRoot }));
  }
  const namespacesDir = path.join(memoryDir, "namespaces");
  let entries;
  try {
    entries = await readdir(namespacesDir, { withFileTypes: true });
  } catch {
    return [...seen.entries()].map(([namespace, storageRoot]) => ({ namespace, storageRoot }));
  }
  for (const entry of entries) {
    if (!entry.isDirectory()) continue;
    if (!isSafeRouteNamespace(entry.name)) continue;
    const root = path.join(namespacesDir, entry.name);
    seen.set(entry.name, root);
  }
  return [...seen.entries()].map(([namespace, storageRoot]) => ({ namespace, storageRoot }));
}
async function runGraphEdgeDecayMaintenanceAcrossNamespaces(memoryDir, options) {
  const { namespacesEnabled, defaultNamespace, ...decayOptions } = options;
  const roots = await discoverGraphNamespaceRoots(memoryDir, {
    namespacesEnabled,
    defaultNamespace
  });
  const results = [];
  for (const { namespace, storageRoot } of roots) {
    try {
      const telemetry = await runGraphEdgeDecayMaintenance(storageRoot, decayOptions);
      results.push({ namespace, storageRoot, telemetry });
    } catch (err) {
      results.push({
        namespace,
        storageRoot,
        error: err instanceof Error ? err.message : String(err)
      });
    }
  }
  return results;
}
export {
  DEFAULT_VISIBILITY_THRESHOLD,
  discoverGraphNamespaceRoots,
  graphEdgeDecayStatusPath,
  readGraphEdgeDecayStatus,
  runGraphEdgeDecayMaintenance,
  runGraphEdgeDecayMaintenanceAcrossNamespaces
};
//# sourceMappingURL=graph-edge-decay-PWB63GRE.js.map