import Graph from "graphology";
import pagerank from "graphology-metrics/centrality/pagerank.js";
class MapxGraph {
  graph;
  repo;
  constructor(repo) {
    this.repo = repo;
    this.graph = new Graph({ type: "directed", multi: true });
  }
  addFileNode(filePath, language, sizeBytes, lines) {
    const nodeId = this.fileNodeId(filePath);
    if (!this.graph.hasNode(nodeId)) {
      this.graph.addNode(nodeId, { type: "file", path: filePath, language, sizeBytes, lines });
    } else {
      this.graph.mergeNodeAttributes(nodeId, { language, sizeBytes, lines });
    }
  }
  addSymbolNode(symbolId, filePath, name, kind, startLine, endLine, scope) {
    const nodeId = this.symbolNodeId(filePath, symbolId);
    if (!this.graph.hasNode(nodeId)) {
      this.graph.addNode(nodeId, { type: "symbol", name, kind, filePath, startLine, endLine, scope });
    } else {
      this.graph.mergeNodeAttributes(nodeId, { kind, startLine, endLine, scope });
    }
    const fileNodeId = this.fileNodeId(filePath);
    if (this.graph.hasNode(fileNodeId) && !this.graph.hasEdge(fileNodeId, nodeId)) {
      this.graph.addDirectedEdge(fileNodeId, nodeId, { type: "contains" });
    }
    if (scope) {
      const parentNodeId = this.symbolNodeId(filePath, scope);
      if (this.graph.hasNode(parentNodeId) && parentNodeId !== nodeId && !this.graph.hasEdge(parentNodeId, nodeId)) {
        this.graph.addDirectedEdge(parentNodeId, nodeId, { type: "contains" });
      }
    }
  }
  addDependencyEdge(edge) {
    const sourceId = this.fileNodeId(edge.sourceFile);
    const targetId = this.fileNodeId(edge.targetFile);
    if (this.graph.hasNode(sourceId) && this.graph.hasNode(targetId)) {
      const edgeKey = `${sourceId}->${targetId}:${edge.edgeType}:${edge.sourceSymbol || ""}`;
      if (!this.graph.hasEdge(edgeKey)) {
        try {
          this.graph.addDirectedEdgeWithKey(edgeKey, sourceId, targetId, {
            type: edge.edgeType,
            sourceSymbol: edge.sourceSymbol,
            targetSymbol: edge.targetSymbol,
            weight: edge.weight,
            verifiability: edge.verifiability || "verified",
            metadata: edge.metadata || {},
            repo: edge.repo,
            targetRepo: edge.targetRepo
          });
        } catch {
        }
      }
    }
  }
  computePageRank() {
    try {
      const scores = pagerank(this.graph, { alpha: 0.85 });
      for (const [node, score] of Object.entries(scores)) {
        if (this.graph.hasNode(node)) {
          this.graph.mergeNodeAttributes(node, { pagerank: score });
        }
      }
      return new Map(Object.entries(scores));
    } catch {
      return /* @__PURE__ */ new Map();
    }
  }
  getRankedFiles() {
    const scores = this.computePageRank();
    const files = [];
    for (const [nodeId, score] of scores) {
      const attrs = this.graph.getNodeAttributes(nodeId);
      if (attrs.type === "file") {
        files.push({ path: attrs.path, pagerank: score, language: attrs.language });
      }
    }
    files.sort((a, b) => b.pagerank - a.pagerank);
    return files;
  }
  getRankedSymbols() {
    const fileRank = this.computePageRank();
    const symbols = [];
    for (const node of this.graph.nodes()) {
      const attrs = this.graph.getNodeAttributes(node);
      if (attrs.type === "symbol") {
        const parentFileRank = fileRank.get(this.fileNodeId(attrs.filePath)) || 0;
        const inDegree = this.graph.inDegree(node);
        const symbolRank = parentFileRank * (1 + inDegree * 0.1);
        symbols.push({
          name: attrs.name,
          kind: attrs.kind,
          filePath: attrs.filePath,
          startLine: attrs.startLine,
          pagerank: symbolRank,
          scope: attrs.scope
        });
      }
    }
    symbols.sort((a, b) => b.pagerank - a.pagerank);
    return symbols;
  }
  getDependencies(filePath) {
    const nodeId = this.fileNodeId(filePath);
    if (!this.graph.hasNode(nodeId)) return [];
    const deps = [];
    for (const edge of this.graph.outEdges(nodeId)) {
      const attrs = this.graph.getEdgeAttributes(edge);
      if (attrs.type !== "contains") {
        const target = this.graph.getNodeAttributes(this.graph.target(edge));
        if (target.type === "file") {
          deps.push({ target: target.path, type: attrs.type });
        }
      }
    }
    return deps;
  }
  getReverseDependencies(filePath) {
    const nodeId = this.fileNodeId(filePath);
    if (!this.graph.hasNode(nodeId)) return [];
    const rdeps = [];
    for (const edge of this.graph.inEdges(nodeId)) {
      const attrs = this.graph.getEdgeAttributes(edge);
      if (attrs.type !== "contains") {
        const source = this.graph.getNodeAttributes(this.graph.source(edge));
        if (source.type === "file") {
          rdeps.push({ source: source.path, type: attrs.type });
        }
      }
    }
    return rdeps;
  }
  getFileCount() {
    let count = 0;
    for (const node of this.graph.nodes()) {
      if (this.graph.getNodeAttribute(node, "type") === "file") count++;
    }
    return count;
  }
  getSymbolCount() {
    let count = 0;
    for (const node of this.graph.nodes()) {
      if (this.graph.getNodeAttribute(node, "type") === "symbol") count++;
    }
    return count;
  }
  getEdgeCount() {
    let count = 0;
    for (const edge of this.graph.edges()) {
      const attrs = this.graph.getEdgeAttributes(edge);
      if (attrs.type !== "contains") count++;
    }
    return count;
  }
  dropFrameworkEdgesForRepo(repoName) {
    const toDrop = [];
    for (const edge of this.graph.edges()) {
      const attrs = this.graph.getEdgeAttributes(edge);
      if (attrs.repo === repoName && ["route", "middleware", "hook", "graphql_resolver", "message_handler", "websocket_handler"].includes(attrs.type)) {
        toDrop.push(edge);
      }
    }
    for (const edge of toDrop) {
      this.graph.dropEdge(edge);
    }
  }
  toJSON() {
    return this.graph.toJSON();
  }
  static fromJSON(data, repo) {
    const cg = new MapxGraph(repo);
    cg.graph.import(data);
    return cg;
  }
  fileNodeId(filePath) {
    return `file://${filePath}`;
  }
  symbolNodeId(filePath, symbolName) {
    return `symbol://${filePath}::${symbolName}`;
  }
}
export {
  MapxGraph
};
