import {
  DEFAULT_HALF_LIFE_MS,
  computeDecayScore
} from "./chunk-ZE473AO6.js";
import {
  MEMORY_FILE_NAME,
  MEMORY_PATTERNS,
  loadMemory
} from "./chunk-DFR4DJBM.js";
import {
  LUMENFLOW_MEMORY_PATHS
} from "./chunk-4N74J3UT.js";
import {
  ErrorCodes,
  createError
} from "./chunk-RXRKBBSM.js";

// ../memory/dist/mem-context-core.js
import path2 from "path";

// ../memory/dist/decay/access-tracking.js
import fs from "fs/promises";
import path from "path";
async function writeMemoryFile(baseDir, nodes) {
  const filePath = path.join(baseDir, MEMORY_FILE_NAME);
  const content = nodes.map((n) => JSON.stringify(n)).join("\n") + (nodes.length > 0 ? "\n" : "");
  await fs.writeFile(filePath, content, { encoding: "utf-8" });
}
function updateNodeAccess(node, now = Date.now(), halfLifeMs = DEFAULT_HALF_LIFE_MS) {
  const timestamp = new Date(now).toISOString();
  const existingAccess = node.metadata?.access;
  const currentCount = existingAccess?.count ?? 0;
  const newAccess = {
    count: currentCount + 1,
    last_accessed_at: timestamp
  };
  const updatedNodeForScoring = {
    ...node,
    metadata: {
      ...node.metadata,
      access: newAccess
    }
  };
  const decayScore = computeDecayScore(updatedNodeForScoring, { now, halfLifeMs });
  const newDecay = {
    score: decayScore,
    computed_at: timestamp
  };
  return {
    ...node,
    metadata: {
      ...node.metadata,
      access: newAccess,
      decay: newDecay
    }
  };
}
async function recordAccessBatch(baseDir, nodeIds, options = {}) {
  const { now = Date.now(), halfLifeMs = DEFAULT_HALF_LIFE_MS } = options;
  const memory = await loadMemory(baseDir);
  const updatedNodes = [];
  const nodeIdSet = new Set(nodeIds);
  const allNodes = memory.nodes.map((node) => {
    if (nodeIdSet.has(node.id)) {
      const updated = updateNodeAccess(node, now, halfLifeMs);
      updatedNodes.push(updated);
      return updated;
    }
    return node;
  });
  if (updatedNodes.length > 0) {
    await writeMemoryFile(baseDir, allNodes);
  }
  return updatedNodes;
}

// ../memory/dist/mem-context-core.js
var DEFAULT_MAX_SIZE = 4096;
var DEFAULT_MAX_PROJECT_NODES = 10;
var DEFAULT_MAX_RECENT_SUMMARIES = 5;
var ERROR_MESSAGES = {
  WU_ID_REQUIRED: "wuId is required",
  WU_ID_EMPTY: "wuId cannot be empty",
  WU_ID_INVALID: "Invalid WU ID format. Expected pattern: WU-XXX (e.g., WU-1234)"
};
var SECTION_HEADERS = {
  PROJECT_PROFILE: "## Project Profile",
  SUMMARIES: "## Summaries",
  WU_CONTEXT: "## WU Context",
  DISCOVERIES: "## Discoveries"
};
function validateWuId(wuId) {
  if (wuId == null) {
    throw createError(ErrorCodes.VALIDATION_ERROR, ERROR_MESSAGES.WU_ID_REQUIRED);
  }
  if (wuId === "") {
    throw createError(ErrorCodes.VALIDATION_ERROR, ERROR_MESSAGES.WU_ID_EMPTY);
  }
  if (!MEMORY_PATTERNS.WU_ID.test(wuId)) {
    throw createError(ErrorCodes.INVALID_WU_ID, ERROR_MESSAGES.WU_ID_INVALID);
  }
}
function compareByRecency(a, b) {
  const aTime = new Date(a.created_at).getTime();
  const bTime = new Date(b.created_at).getTime();
  if (aTime !== bTime) {
    return bTime - aTime;
  }
  return a.id.localeCompare(b.id);
}
function createCompareByDecay(now, halfLifeMs) {
  return (a, b) => {
    const aScore = computeDecayScore(a, { now, halfLifeMs });
    const bScore = computeDecayScore(b, { now, halfLifeMs });
    if (aScore !== bScore) {
      return bScore - aScore;
    }
    return a.id.localeCompare(b.id);
  };
}
function filterByLifecycle(nodes, lifecycle) {
  return nodes.filter((node) => node.lifecycle === lifecycle);
}
function filterByWuId(nodes, wuId) {
  return nodes.filter((node) => node.wu_id === wuId);
}
function filterByType(nodes, type) {
  return nodes.filter((node) => node.type === type);
}
function filterByLane(nodes, lane) {
  if (!lane) {
    return nodes;
  }
  return nodes.filter((node) => {
    const nodeLane = node.metadata?.lane;
    return !nodeLane || nodeLane === lane;
  });
}
function limitNodes(nodes, limit) {
  if (limit === void 0 || limit <= 0) {
    return nodes;
  }
  return nodes.slice(0, limit);
}
function formatNode(node) {
  const timestamp = new Date(node.created_at).toISOString().split("T")[0];
  return `- [${node.id}] (${timestamp}): ${node.content}`;
}
function formatSection(header, nodes) {
  if (nodes.length === 0) {
    return "";
  }
  const lines = [header, ""];
  for (const node of nodes) {
    lines.push(formatNode(node));
  }
  lines.push("");
  return lines.join("\n");
}
function buildHeader(wuId) {
  return `<!-- mem:context for ${wuId} -->

`;
}
function createEmptyResult() {
  return {
    success: true,
    contextBlock: "",
    stats: {
      totalNodes: 0,
      byType: {},
      truncated: false,
      size: 0
    }
  };
}
function addSectionIfNotEmpty(sections, header, nodes) {
  const section = formatSection(header, nodes);
  if (section) {
    sections.push(section);
  }
}
function truncateToSize(contextBlock, maxSize) {
  if (contextBlock.length <= maxSize) {
    return { content: contextBlock, truncated: false };
  }
  const lines = contextBlock.split("\n");
  let currentSize = 0;
  const truncatedLines = [];
  let truncated = false;
  for (const line of lines) {
    const lineSize = line.length + 1;
    if (currentSize + lineSize > maxSize) {
      truncated = true;
      break;
    }
    truncatedLines.push(line);
    currentSize += lineSize;
  }
  if (truncated) {
    truncatedLines.push("");
    truncatedLines.push("<!-- truncated - context exceeded max size -->");
  }
  return { content: truncatedLines.join("\n"), truncated };
}
async function generateContext(baseDir, options) {
  const {
    wuId,
    maxSize = DEFAULT_MAX_SIZE,
    sortByDecay = false,
    trackAccess = false,
    halfLifeMs = DEFAULT_HALF_LIFE_MS,
    now = Date.now(),
    // WU-1281: New options for lane filtering and limits
    lane,
    maxRecentSummaries = DEFAULT_MAX_RECENT_SUMMARIES,
    maxProjectNodes = DEFAULT_MAX_PROJECT_NODES
  } = options;
  validateWuId(wuId);
  const memoryDir = path2.join(baseDir, LUMENFLOW_MEMORY_PATHS.MEMORY_DIR);
  const allNodes = await loadNodesOrEmpty(memoryDir);
  if (allNodes.length === 0) {
    return createEmptyResult();
  }
  const sortComparator = sortByDecay ? createCompareByDecay(now, halfLifeMs) : compareByRecency;
  const summaryNodes = limitNodes([...filterByWuId(filterByType(allNodes, "summary"), wuId)].sort(sortComparator), maxRecentSummaries);
  const wuContextNodes = [...filterByWuId(allNodes, wuId)].filter((node) => node.type !== "summary" && node.type !== "discovery").sort(sortComparator);
  const discoveryNodes = [...filterByWuId(filterByType(allNodes, "discovery"), wuId)].sort(sortComparator);
  const projectNodes = limitNodes(filterByLane([...filterByLifecycle(allNodes, "project")].sort(sortComparator), lane), maxProjectNodes);
  const sections = [buildHeader(wuId)];
  addSectionIfNotEmpty(sections, SECTION_HEADERS.WU_CONTEXT, wuContextNodes);
  addSectionIfNotEmpty(sections, SECTION_HEADERS.SUMMARIES, summaryNodes);
  addSectionIfNotEmpty(sections, SECTION_HEADERS.DISCOVERIES, discoveryNodes);
  addSectionIfNotEmpty(sections, SECTION_HEADERS.PROJECT_PROFILE, projectNodes);
  if (sections.length === 1) {
    return createEmptyResult();
  }
  const rawContextBlock = sections.join("");
  const { content: contextBlock, truncated } = truncateToSize(rawContextBlock, maxSize);
  const selectedNodes = [...wuContextNodes, ...summaryNodes, ...discoveryNodes, ...projectNodes];
  const byType = {};
  for (const node of selectedNodes) {
    byType[node.type] = (byType[node.type] || 0) + 1;
  }
  const accessTracked = await trackAccessIfEnabled(trackAccess, selectedNodes, memoryDir, now, halfLifeMs);
  return {
    success: true,
    contextBlock,
    stats: {
      totalNodes: selectedNodes.length,
      byType,
      truncated,
      size: contextBlock.length,
      ...trackAccess ? { accessTracked } : {}
    }
  };
}
async function loadNodesOrEmpty(memoryDir) {
  try {
    const memory = await loadMemory(memoryDir);
    return memory.nodes;
  } catch (error) {
    const err = error;
    if (err.code === "ENOENT") {
      return [];
    }
    throw error;
  }
}
async function trackAccessIfEnabled(trackAccess, nodes, memoryDir, now, halfLifeMs) {
  if (!trackAccess || nodes.length === 0) {
    return 0;
  }
  try {
    const nodeIds = nodes.map((n) => n.id);
    const tracked = await recordAccessBatch(memoryDir, nodeIds, { now, halfLifeMs });
    return tracked.length;
  } catch {
    return 0;
  }
}

export {
  generateContext
};
