import {
  detectCycles
} from "./chunk-2GXVIN57.js";
import {
  readWU,
  readWUAsync
} from "./chunk-NRIZR3A7.js";
import {
  WU_PATHS
} from "./chunk-6HO4GWJE.js";
import {
  STRING_LITERALS
} from "./chunk-DWMLTXKQ.js";
import {
  WU_STATUS
} from "./chunk-V6OJGLBA.js";

// ../core/dist/dependency-graph.js
import { existsSync, readdirSync, promises as fs } from "fs";
import path from "path";
var GRAPH_STATUS_MAP = {
  done: WU_STATUS.DONE,
  in_progress: WU_STATUS.IN_PROGRESS,
  ready: WU_STATUS.READY,
  blocked: WU_STATUS.BLOCKED
};
function toStringArray(value) {
  if (!Array.isArray(value)) {
    return [];
  }
  return value.filter((entry) => typeof entry === "string");
}
function toDependencyNode(id, doc) {
  return {
    id,
    title: typeof doc.title === "string" ? doc.title : id,
    status: typeof doc.status === "string" ? doc.status : "unknown",
    blocks: toStringArray(doc.blocks),
    blockedBy: toStringArray(doc.blocked_by)
  };
}
function isGraphStatus(status) {
  return status === GRAPH_STATUS_MAP.done || status === GRAPH_STATUS_MAP.in_progress || status === GRAPH_STATUS_MAP.ready || status === GRAPH_STATUS_MAP.blocked;
}
function toCycleDetectorGraph(graph) {
  const normalized = /* @__PURE__ */ new Map();
  for (const [id, node] of graph.entries()) {
    normalized.set(id, {
      id: node.id,
      blocks: node.blocks,
      blocked_by: node.blockedBy
    });
  }
  return normalized;
}
function buildDependencyGraph() {
  const wuDir = path.dirname(WU_PATHS.WU("dummy"));
  const graph = /* @__PURE__ */ new Map();
  if (!existsSync(wuDir)) {
    return graph;
  }
  const files = readdirSync(wuDir).filter((f) => f.endsWith(".yaml") && f.startsWith("WU-"));
  for (const f of files) {
    const filePath = path.join(wuDir, f);
    const id = f.replace(".yaml", "");
    try {
      const doc = readWU(filePath, id);
      graph.set(id, toDependencyNode(id, doc));
    } catch {
    }
  }
  return graph;
}
async function buildDependencyGraphAsync() {
  const wuDir = path.dirname(WU_PATHS.WU("dummy"));
  const graph = /* @__PURE__ */ new Map();
  try {
    const files = await fs.readdir(wuDir);
    const yamlFiles = files.filter((f) => f.endsWith(".yaml") && f.startsWith("WU-"));
    const promises = yamlFiles.map(async (f) => {
      const filePath = path.join(wuDir, f);
      const id = f.replace(".yaml", "");
      try {
        const doc = await readWUAsync(filePath, id);
        return toDependencyNode(id, doc);
      } catch {
        return null;
      }
    });
    const results = await Promise.all(promises);
    for (const result of results) {
      if (result) {
        graph.set(result.id, result);
      }
    }
  } catch {
  }
  return graph;
}
function getUpstreamDependencies(graph, wuId, maxDepth = 10) {
  const visited = /* @__PURE__ */ new Set();
  const result = [];
  function traverse(id, depth, pathSoFar) {
    if (depth > maxDepth || visited.has(id))
      return;
    visited.add(id);
    const node = graph.get(id);
    if (!node)
      return;
    for (const dep of node.blockedBy) {
      if (!visited.has(dep)) {
        result.push({ id: dep, depth, path: [...pathSoFar, dep] });
        traverse(dep, depth + 1, [...pathSoFar, dep]);
      }
    }
  }
  traverse(wuId, 1, [wuId]);
  return result;
}
function getDownstreamDependents(graph, wuId, maxDepth = 10) {
  const visited = /* @__PURE__ */ new Set();
  const result = [];
  function traverse(id, depth, pathSoFar) {
    if (depth > maxDepth || visited.has(id))
      return;
    visited.add(id);
    const node = graph.get(id);
    if (!node)
      return;
    for (const dep of node.blocks) {
      if (!visited.has(dep)) {
        result.push({ id: dep, depth, path: [...pathSoFar, dep] });
        traverse(dep, depth + 1, [...pathSoFar, dep]);
      }
    }
  }
  traverse(wuId, 1, [wuId]);
  return result;
}
function renderASCII(graph, rootId, options = {}) {
  const { direction = "both", depth: maxDepth = 3 } = options;
  const lines = [];
  const root = graph.get(rootId);
  if (!root) {
    return `WU not found: ${rootId}`;
  }
  lines.push(`${rootId}: ${truncate(root.title, 50)}`);
  lines.push("");
  if (direction === "up" || direction === "both") {
    const upstream = getUpstreamDependencies(graph, rootId, maxDepth);
    if (upstream.length > 0) {
      lines.push("Dependencies (blocked by):");
      for (const dep of upstream) {
        const node = graph.get(dep.id);
        const status = node ? `[${node.status}]` : "[unknown]";
        const title = node ? truncate(node.title, 40) : "";
        const indent = "  ".repeat(dep.depth);
        lines.push(`${indent}+-- ${dep.id}: ${title} ${status}`);
      }
      lines.push("");
    }
  }
  if (direction === "down" || direction === "both") {
    const downstream = getDownstreamDependents(graph, rootId, maxDepth);
    if (downstream.length > 0) {
      lines.push("Dependents (blocks):");
      for (const dep of downstream) {
        const node = graph.get(dep.id);
        const status = node ? `[${node.status}]` : "[unknown]";
        const title = node ? truncate(node.title, 40) : "";
        const indent = "  ".repeat(dep.depth);
        lines.push(`${indent}+-- ${dep.id}: ${title} ${status}`);
      }
      lines.push("");
    }
  }
  const cycleResult = detectCycles(toCycleDetectorGraph(graph));
  if (cycleResult.hasCycle) {
    lines.push("\u26A0\uFE0F  Circular dependencies detected:");
    for (const cycle of cycleResult.cycles) {
      lines.push(`  ${cycle.join(" \u2192 ")}`);
    }
    lines.push("");
  }
  return lines.join(STRING_LITERALS.NEWLINE);
}
function renderMermaid(graph, options = {}) {
  const { rootId, direction = "TD", depth: maxDepth = 3 } = options;
  const lines = [];
  const nodes = /* @__PURE__ */ new Set();
  const edges = [];
  if (rootId) {
    const upstream = getUpstreamDependencies(graph, rootId, maxDepth);
    const downstream = getDownstreamDependents(graph, rootId, maxDepth);
    nodes.add(rootId);
    for (const dep of upstream) {
      nodes.add(dep.id);
    }
    for (const dep of downstream) {
      nodes.add(dep.id);
    }
    for (const nodeId of nodes) {
      const node = graph.get(nodeId);
      if (!node)
        continue;
      for (const blockedBy of node.blockedBy) {
        if (nodes.has(blockedBy)) {
          edges.push({ from: blockedBy, to: nodeId });
        }
      }
    }
  } else {
    for (const [nodeId, node] of graph.entries()) {
      nodes.add(nodeId);
      for (const blockedBy of node.blockedBy) {
        if (graph.has(blockedBy)) {
          edges.push({ from: blockedBy, to: nodeId });
        }
      }
    }
  }
  lines.push(`flowchart ${direction}`);
  for (const nodeId of nodes) {
    const node = graph.get(nodeId);
    if (node) {
      const shortTitle = truncate(node.title, 30);
      lines.push(`    ${nodeId}["${nodeId}: ${shortTitle}<br/>${node.status}"]`);
    }
  }
  lines.push("");
  for (const { from, to } of edges) {
    lines.push(`    ${from} --> ${to}`);
  }
  lines.push("");
  lines.push("    classDef done fill:#86efac,stroke:#22c55e");
  lines.push("    classDef in_progress fill:#93c5fd,stroke:#3b82f6");
  lines.push("    classDef ready fill:#fde68a,stroke:#f59e0b");
  lines.push("    classDef blocked fill:#fca5a5,stroke:#ef4444");
  const statusGroups = {
    done: [],
    in_progress: [],
    ready: [],
    blocked: []
  };
  for (const nodeId of nodes) {
    const node = graph.get(nodeId);
    if (node && isGraphStatus(node.status)) {
      statusGroups[node.status].push(nodeId);
    }
  }
  for (const [status, nodeIds] of Object.entries(statusGroups)) {
    if (nodeIds.length > 0) {
      lines.push(`    class ${nodeIds.join(",")} ${status}`);
    }
  }
  return lines.join(STRING_LITERALS.NEWLINE);
}
function validateGraph(graph) {
  const allIds = new Set(graph.keys());
  const orphans = [];
  for (const [wuId, node] of graph.entries()) {
    for (const ref of [...node.blocks, ...node.blockedBy]) {
      if (!allIds.has(ref)) {
        orphans.push({ wuId, ref });
      }
    }
  }
  const cycleResult = detectCycles(toCycleDetectorGraph(graph));
  return {
    hasCycle: cycleResult.hasCycle,
    cycles: cycleResult.cycles,
    orphans
  };
}
function truncate(str, maxLen) {
  if (!str)
    return "";
  return str.length > maxLen ? `${str.substring(0, maxLen - 3)}...` : str;
}

export {
  buildDependencyGraph,
  buildDependencyGraphAsync,
  renderASCII,
  renderMermaid,
  validateGraph
};
