import {
  appendNode,
  loadMemory
} from "./chunk-DFR4DJBM.js";
import {
  validateLaneFormat
} from "./chunk-JONWQUB5.js";
import {
  LUMENFLOW_MEMORY_PATHS
} from "./chunk-4N74J3UT.js";
import {
  createWuPaths
} from "./chunk-6HO4GWJE.js";
import {
  ErrorCodes,
  createError
} from "./chunk-RXRKBBSM.js";

// ../memory/dist/mem-triage-core.js
import fs from "fs/promises";
import path from "path";
var RELATIONSHIPS_FILE_NAME = "relationships.jsonl";
var DEFAULT_PRIORITY = "P2";
var MAX_TITLE_LENGTH = 80;
var PRIORITY_RANK = {
  P0: 0,
  P1: 1,
  P2: 2,
  P3: 3
};
var DEFAULT_PRIORITY_RANK = 999;
function getPriorityRank(node) {
  const priority = node.metadata?.priority;
  if (!priority) {
    return DEFAULT_PRIORITY_RANK;
  }
  return PRIORITY_RANK[priority] ?? DEFAULT_PRIORITY_RANK;
}
function compareNodes(a, b) {
  const priorityDiff = getPriorityRank(a) - getPriorityRank(b);
  if (priorityDiff !== 0) {
    return priorityDiff;
  }
  const aTime = new Date(a.created_at).getTime();
  const bTime = new Date(b.created_at).getTime();
  if (aTime !== bTime) {
    return aTime - bTime;
  }
  return a.id.localeCompare(b.id);
}
async function loadRelationships(memoryDir) {
  const filePath = path.join(memoryDir, RELATIONSHIPS_FILE_NAME);
  try {
    const content = await fs.readFile(filePath, { encoding: "utf-8" });
    const lines = content.split("\n");
    const relationships = [];
    for (const line of lines) {
      const trimmed = line.trim();
      if (!trimmed)
        continue;
      try {
        relationships.push(JSON.parse(trimmed));
      } catch {
        continue;
      }
    }
    return relationships;
  } catch (err) {
    const error = err;
    if (error.code === "ENOENT") {
      return [];
    }
    throw error;
  }
}
function buildBlockedSet(relationships) {
  const blocked = /* @__PURE__ */ new Set();
  for (const rel of relationships) {
    if (rel.type === "blocks") {
      blocked.add(rel.to_id);
    }
  }
  return blocked;
}
function isBlocked(node, blockedByRelationships) {
  if (blockedByRelationships.has(node.id)) {
    return true;
  }
  const blockedBy = node.metadata?.blocked_by;
  if (Array.isArray(blockedBy) && blockedBy.length > 0) {
    return true;
  }
  return false;
}
function isClosed(node) {
  const status = node.metadata?.status;
  if (status === "closed" || status === "archived") {
    return true;
  }
  if (node.lifecycle === "ephemeral") {
    return true;
  }
  return false;
}
async function listOpenDiscoveries(baseDir, options = {}) {
  const memoryDir = path.join(baseDir, LUMENFLOW_MEMORY_PATHS.MEMORY_DIR);
  const memory = await loadMemory(memoryDir);
  const relationships = await loadRelationships(memoryDir);
  const blockedByRelationships = buildBlockedSet(relationships);
  let nodes = memory.nodes.filter((node) => node.type === "discovery");
  nodes = nodes.filter((node) => !isBlocked(node, blockedByRelationships));
  nodes = nodes.filter((node) => !isClosed(node));
  if (options.wuId) {
    if (options.wuId === "unlinked") {
      nodes = nodes.filter((node) => !node.wu_id);
    } else {
      nodes = nodes.filter((node) => node.wu_id === options.wuId);
    }
  }
  if (options.tag) {
    const filterTag = options.tag;
    nodes = nodes.filter((node) => node.tags?.includes(filterTag));
  }
  return nodes.sort(compareNodes);
}
async function archiveDiscovery(baseDir, options) {
  const { nodeId, reason } = options;
  const memoryDir = path.join(baseDir, LUMENFLOW_MEMORY_PATHS.MEMORY_DIR);
  const memory = await loadMemory(memoryDir);
  const node = memory.byId.get(nodeId);
  if (!node) {
    throw createError(ErrorCodes.NODE_NOT_FOUND, `Node not found: ${nodeId}`);
  }
  if (node.type !== "discovery") {
    throw createError(ErrorCodes.VALIDATION_ERROR, `Node ${nodeId} is not a discovery (type: ${node.type})`);
  }
  if (node.metadata?.status === "archived" || node.metadata?.status === "closed") {
    throw createError(ErrorCodes.ALREADY_EXISTS, `Node ${nodeId} is already archived/closed`);
  }
  const archivedNode = {
    ...node,
    metadata: {
      ...node.metadata,
      status: "archived",
      archive_reason: reason,
      archived_at: (/* @__PURE__ */ new Date()).toISOString()
    }
  };
  await appendNode(memoryDir, archivedNode);
  return {
    success: true,
    nodeId
  };
}
async function getNextWuId(baseDir) {
  const paths = createWuPaths({ projectRoot: baseDir });
  const wuDir = path.join(baseDir, paths.WU_DIR());
  let maxId = 0;
  try {
    const files = await fs.readdir(wuDir);
    for (const file of files) {
      const match = file.match(/^WU-(\d+)\.yaml$/);
      if (match && match[1]) {
        const id = parseInt(match[1], 10);
        if (id > maxId) {
          maxId = id;
        }
      }
    }
  } catch (err) {
    const error = err;
    if (error.code !== "ENOENT") {
      throw error;
    }
  }
  return `WU-${maxId + 1}`;
}
function truncateToTitle(content) {
  const parts = content.split(/[.!?]/);
  const firstSentence = (parts[0] ?? "").trim();
  if (firstSentence.length <= MAX_TITLE_LENGTH) {
    return firstSentence;
  }
  return firstSentence.substring(0, MAX_TITLE_LENGTH - 3) + "...";
}
async function promoteDiscovery(baseDir, options) {
  const { nodeId, lane, title, wuId, priority, dryRun: _dryRun = false } = options;
  const memoryDir = path.join(baseDir, LUMENFLOW_MEMORY_PATHS.MEMORY_DIR);
  try {
    validateLaneFormat(lane);
  } catch (err) {
    const errMsg = err instanceof Error ? err.message : String(err);
    throw createError(ErrorCodes.INVALID_LANE, `Invalid lane format: ${errMsg}`, { cause: err });
  }
  const memory = await loadMemory(memoryDir);
  const node = memory.byId.get(nodeId);
  if (!node) {
    throw createError(ErrorCodes.NODE_NOT_FOUND, `Node not found: ${nodeId}`);
  }
  if (node.type !== "discovery") {
    throw createError(ErrorCodes.VALIDATION_ERROR, `Node ${nodeId} is not a discovery (type: ${node.type})`);
  }
  if (node.metadata?.status === "archived" || node.metadata?.status === "closed") {
    throw createError(ErrorCodes.ALREADY_EXISTS, `Node ${nodeId} is already archived/closed`);
  }
  const resolvedWuId = wuId || await getNextWuId(baseDir);
  const resolvedTitle = title || truncateToTitle(node.content);
  const resolvedPriority = priority || node.metadata?.priority || DEFAULT_PRIORITY;
  const wuSpec = {
    id: resolvedWuId,
    title: resolvedTitle,
    lane,
    priority: resolvedPriority,
    notes: `Promoted from discovery ${nodeId}`
  };
  return {
    success: true,
    wuSpec
  };
}

export {
  listOpenDiscoveries,
  archiveDiscovery,
  promoteDiscovery
};
