// ═══════════════════════════════════════════════════════════════
// Attack Chain DAG Visualization — vis-network wrapper
// Obsidian Control theme
// ═══════════════════════════════════════════════════════════════

(function () {
  "use strict";

  // Status color palette matching Obsidian Control
  var STATUS_COLORS = {
    completed: { bg: "#16a34a", border: "#12873c", font: "#060a13" },
    running:   { bg: "#d97706", border: "#b56205", font: "#060a13" },
    failed:    { bg: "#dc2626", border: "#b91c1c", font: "#ffffff" },
    pending:   { bg: "#5e7291", border: "#3d506b", font: "#e4eaf4" },
    skipped:   { bg: "#374151", border: "#1f2937", font: "#e4eaf4" },
  };

  var EDGE_COLOR = "rgba(94, 114, 145, 0.5)";

  var network = null;
  var nodesDataset = null;
  var edgesDataset = null;
  var rawNodesMap = {};

  function getStatusColor(status) {
    return STATUS_COLORS[status] || STATUS_COLORS.pending;
  }

  function truncate(str, maxLen) {
    if (!str) return "";
    return str.length > maxLen ? str.substring(0, maxLen) + "…" : str;
  }

  function buildTooltip(node) {
    var lines = [];
    lines.push("Tool: " + (node.tool_name || "unknown"));
    lines.push("Status: " + (node.status || "pending"));
    if (node.llm_reasoning) {
      lines.push("Reasoning: " + truncate(node.llm_reasoning, 200));
    }
    var findingsCount = node.findings_produced ? node.findings_produced.length : 0;
    lines.push("Findings: " + findingsCount);
    lines.push("Risk: " + (node.risk_score != null ? node.risk_score : "N/A"));
    return lines.join("\n");
  }

  function toVisNode(node) {
    var color = getStatusColor(node.status);
    // Scale node size by risk_score: min 12, max 40
    var risk = typeof node.risk_score === "number" ? node.risk_score : 0;
    var size = Math.max(12, Math.min(40, 12 + (risk / 100) * 28));

    return {
      id: node.node_id,
      label: node.tool_name || "unknown",
      title: buildTooltip(node),
      size: size,
      color: {
        background: color.bg,
        border: color.border,
        highlight: { background: color.bg, border: "#ffffff" },
        hover: { background: color.bg, border: "#ffffff" },
      },
      font: {
        color: color.font,
        size: 11,
        face: "'JetBrains Mono', monospace",
        strokeWidth: 2,
        strokeColor: "#060a13",
      },
      borderWidth: 1,
      borderWidthSelected: 3,
      shape: "dot",
      _raw: node,
    };
  }

  function buildEdges(nodes) {
    var edges = [];
    var seen = {};
    nodes.forEach(function (node) {
      if (!node.parent_node_ids) return;
      node.parent_node_ids.forEach(function (parentId) {
        var key = parentId + "|" + node.node_id;
        if (seen[key]) return;
        seen[key] = true;
        edges.push({
          from: parentId,
          to: node.node_id,
          color: {
            color: EDGE_COLOR,
            highlight: "#ffffff",
            hover: EDGE_COLOR,
          },
          width: 2,
          arrows: {
            to: { enabled: true, scaleFactor: 0.5 },
          },
          smooth: {
            enabled: true,
            type: "cubicBezier",
            roundness: 0.5,
            forceDirection: "vertical",
          },
        });
      });
    });
    return edges;
  }

  /**
   * Load and render an attack chain DAG.
   * Called from Rust via eval().
   * @param {Array} nodes - Array of AttackChainNode objects
   */
  window.__loadAttackChain = function (nodes) {
    var container = document.getElementById("attack-chain-canvas");
    if (!container) {
      console.error("[attack-chain-viz] #attack-chain-canvas not found");
      return;
    }

    // Build lookup map
    rawNodesMap = {};
    nodes.forEach(function (n) {
      rawNodesMap[n.node_id] = n;
    });

    var visNodes = nodes.map(toVisNode);
    var visEdges = buildEdges(nodes);

    nodesDataset = new vis.DataSet(visNodes);
    edgesDataset = new vis.DataSet(visEdges);

    var options = {
      nodes: {
        font: { color: "#e4eaf4", size: 11 },
        scaling: { min: 12, max: 40 },
      },
      edges: {
        font: { color: "#5e7291", size: 9, strokeWidth: 0 },
        selectionWidth: 3,
      },
      physics: {
        enabled: false,
      },
      layout: {
        hierarchical: {
          enabled: true,
          direction: "UD",
          sortMethod: "directed",
          levelSeparation: 120,
          nodeSpacing: 160,
          treeSpacing: 200,
          blockShifting: true,
          edgeMinimization: true,
          parentCentralization: true,
        },
      },
      interaction: {
        hover: true,
        tooltipDelay: 200,
        hideEdgesOnDrag: false,
        hideEdgesOnZoom: false,
        multiselect: false,
        navigationButtons: false,
        keyboard: { enabled: true },
      },
    };

    // Destroy previous instance
    if (network) {
      network.destroy();
    }

    network = new vis.Network(
      container,
      { nodes: nodesDataset, edges: edgesDataset },
      options
    );

    // Click handler — sends data to Rust
    network.on("click", function (params) {
      if (params.nodes.length > 0) {
        var nodeId = params.nodes[0];
        var visNode = nodesDataset.get(nodeId);
        if (visNode && visNode._raw && window.__onAttackNodeClick) {
          window.__onAttackNodeClick(JSON.stringify(visNode._raw));
        }
      }
    });

    console.log(
      "[attack-chain-viz] Loaded " + nodes.length + " nodes, " + visEdges.length + " edges"
    );
  };

  /**
   * Callback placeholder for Rust to set.
   * Called with JSON string of the clicked node's data.
   */
  window.__onAttackNodeClick = null;

  /**
   * Fit entire attack chain DAG in view.
   */
  window.__fitAttackChain = function () {
    if (!network) return;
    network.fit({
      animation: { duration: 400, easingFunction: "easeInOutQuad" },
    });
  };

  /**
   * Select and focus on a specific node by node_id.
   */
  window.__highlightAttackNode = function (nodeId) {
    if (!network || !nodesDataset) return;

    var node = nodesDataset.get(nodeId);
    if (!node) return;

    network.selectNodes([nodeId]);
    network.focus(nodeId, {
      scale: 1.5,
      animation: { duration: 500, easingFunction: "easeInOutQuad" },
    });

    // Trigger click callback too
    if (node._raw && window.__onAttackNodeClick) {
      window.__onAttackNodeClick(JSON.stringify(node._raw));
    }
  };
})();
