class DotExporter {
  store;
  graph;
  constructor(store, graph) {
    this.store = store;
    this.graph = graph;
  }
  export(repo, filesFilter, opts) {
    const clusterMode = opts?.cluster ?? "none";
    const maxClusterDepth = opts?.depth ?? Infinity;
    let files = this.store.getAllFiles(repo);
    let edges = this.store.getAllEdges(repo);
    let rankedFiles = this.graph.getRankedFiles();
    if (filesFilter) {
      const allowed = new Set(filesFilter);
      files = files.filter((f) => allowed.has(f.path));
      edges = edges.filter((e) => allowed.has(e.source_file) && allowed.has(e.target_file));
      rankedFiles = rankedFiles.filter((f) => allowed.has(f.path));
    }
    const lines = [];
    lines.push("digraph Mapx {");
    lines.push("  newrank=true;");
    lines.push("  rankdir=LR;");
    lines.push("  node [shape=box, style=filled];");
    lines.push("");
    const langColors = {
      php: "#8892BF",
      javascript: "#F7DF1E",
      typescript: "#3178C6",
      python: "#3776AB",
      go: "#00ADD8",
      rust: "#DEA584",
      java: "#ED8B00"
    };
    const rankMap = new Map(rankedFiles.map((f) => [f.path, f.pagerank]));
    const clusters = this.store.getClusters(repo);
    const memberships = this.store.getClusterMemberships(repo);
    const primaryMemberships = /* @__PURE__ */ new Map();
    for (const m of memberships) {
      if (m.is_primary === 1) {
        primaryMemberships.set(m.file_path, m.cluster_name);
      }
    }
    const roots = [];
    const childrenMap = /* @__PURE__ */ new Map();
    const clusterFilesMap = /* @__PURE__ */ new Map();
    for (const c of clusters) {
      if (!c.parent_name) {
        roots.push(c);
      } else {
        const parentName = c.parent_name;
        if (!childrenMap.has(parentName)) {
          childrenMap.set(parentName, []);
        }
        childrenMap.get(parentName).push(c);
      }
    }
    for (const file of files) {
      const path = file.path;
      const clusterName = primaryMemberships.get(path);
      if (clusterName) {
        if (!clusterFilesMap.has(clusterName)) {
          clusterFilesMap.set(clusterName, []);
        }
        clusterFilesMap.get(clusterName).push(path);
      }
    }
    const outputCluster = (node, indent, currentDepth) => {
      const pad = "  ".repeat(indent);
      const subLines = [];
      const safeId = "cluster_" + node.name.replace(/[^a-zA-Z0-9]/g, "_");
      subLines.push(`${pad}subgraph "${safeId}" {`);
      subLines.push(`${pad}  label="${node.label}";`);
      subLines.push(`${pad}  color=gray;`);
      subLines.push(`${pad}  style=dashed;`);
      const fList = clusterFilesMap.get(node.name) || [];
      for (const f of fList) {
        const lang = files.find((file) => file.path === f)?.language;
        const color = langColors[lang] || "#CCCCCC";
        const label = f.split("/").pop() || f;
        subLines.push(`${pad}  "${f}" [label="${label}", fillcolor="${color}", fontcolor="white"];`);
      }
      const children = childrenMap.get(node.name) || [];
      if (currentDepth < maxClusterDepth) {
        for (const child of children) {
          subLines.push(...outputCluster(child, indent + 1, currentDepth + 1));
        }
      } else {
        const flattenFiles = (c) => {
          const fList2 = clusterFilesMap.get(c.name) || [];
          for (const f2 of fList2) {
            const lang2 = files.find((file) => file.path === f2)?.language;
            const color2 = langColors[lang2] || "#CCCCCC";
            const label2 = f2.split("/").pop() || f2;
            subLines.push(`${pad}  "${f2}" [label="${label2}", fillcolor="${color2}", fontcolor="white"];`);
          }
          for (const gc of childrenMap.get(c.name) || []) {
            flattenFiles(gc);
          }
        };
        for (const child of children) {
          flattenFiles(child);
        }
      }
      subLines.push(`${pad}}`);
      return subLines;
    };
    if (clusterMode === "none") {
      for (const file of files) {
        const path = file.path;
        const lang = file.language;
        const color = langColors[lang] || "#CCCCCC";
        const label = path.split("/").pop() || path;
        lines.push(`  "${path}" [label="${label}", fillcolor="${color}", fontcolor="white"];`);
      }
    } else {
      for (const file of files) {
        const path = file.path;
        if (!primaryMemberships.has(path)) {
          const lang = file.language;
          const color = langColors[lang] || "#CCCCCC";
          const label = path.split("/").pop() || path;
          lines.push(`  "${path}" [label="${label}", fillcolor="${color}", fontcolor="white"];`);
        }
      }
      for (const root of roots) {
        lines.push(...outputCluster(root, 1, 1));
      }
    }
    lines.push("");
    const edgeStyles = {
      import: "solid",
      require: "solid",
      extends: "bold",
      implements: "dashed",
      call: "dotted",
      instantiation: "dotted",
      relation: "bold",
      route: "solid",
      binding: "dashed",
      middleware: "dotted",
      dispatch: "dashed",
      notify: "dotted",
      render: "dotted"
    };
    const seen = /* @__PURE__ */ new Set();
    for (const edge of edges) {
      const src = edge.source_file;
      const tgt = edge.target_file;
      const type = edge.edge_type;
      const key = `${src}->${tgt}:${type}`;
      if (seen.has(key)) continue;
      seen.add(key);
      let style = edgeStyles[type] || "solid";
      if (edge.verifiability === "inferred") {
        style = "dashed";
      }
      let colorAttr = "";
      if (type === "relation") {
        colorAttr = ', color="blue"';
      } else if (type === "route") {
        colorAttr = ', color="green"';
      } else if (type === "binding") {
        colorAttr = ', color="purple"';
      } else if (type === "middleware") {
        colorAttr = ', color="orange"';
      } else if (type === "dispatch") {
        colorAttr = ', color="magenta"';
      } else if (type === "notify") {
        colorAttr = ', color="pink"';
      } else if (type === "render") {
        colorAttr = ', color="cyan"';
      }
      lines.push(`  "${src}" -> "${tgt}" [label="${type}", style=${style}${colorAttr}];`);
    }
    lines.push("}");
    return lines.join("\n");
  }
}
export {
  DotExporter
};
