import { ConflictDetector } from "./conflict-detector.js";
class StackDiffVisualizer {
  conflictDetector;
  constructor() {
    this.conflictDetector = new ConflictDetector();
  }
  /**
   * Visualize divergence between two frame stacks
   */
  visualizeDivergence(baseFrame, branch1, branch2) {
    const nodes = [];
    const edges = [];
    nodes.push({
      id: baseFrame.frame_id,
      type: "common",
      frame: baseFrame,
      position: { x: 0, y: 0 },
      metadata: { label: "Common Ancestor" }
    });
    const branch1Nodes = this.processBranch(
      branch1,
      baseFrame.frame_id,
      -100,
      // Left side
      100
    );
    nodes.push(...branch1Nodes.nodes);
    edges.push(...branch1Nodes.edges);
    const branch2Nodes = this.processBranch(
      branch2,
      baseFrame.frame_id,
      100,
      // Right side
      100
    );
    nodes.push(...branch2Nodes.nodes);
    edges.push(...branch2Nodes.edges);
    const conflicts = this.conflictDetector.detectConflicts(branch1, branch2);
    this.markConflicts(nodes, edges, conflicts);
    return {
      nodes,
      edges,
      layout: "tree"
    };
  }
  /**
   * Render conflict markers for visualization
   */
  renderConflictMarkers(conflicts) {
    const markers = [];
    for (const conflict of conflicts) {
      markers.push({
        frameId: conflict.frameId1,
        type: "conflict",
        color: this.getSeverityColor(conflict.severity),
        symbol: this.getConflictSymbol(conflict.type),
        description: conflict.description
      });
      markers.push({
        frameId: conflict.frameId2,
        type: "conflict",
        color: this.getSeverityColor(conflict.severity),
        symbol: this.getConflictSymbol(conflict.type),
        description: conflict.description
      });
    }
    return markers;
  }
  /**
   * Generate a merge preview based on resolution strategy
   */
  generateMergePreview(stack1, stack2, strategy) {
    const mergedFrames = [];
    const keptFromStack1 = [];
    const keptFromStack2 = [];
    const conflicts = this.conflictDetector.detectConflicts(stack1, stack2);
    switch (strategy) {
      case "keep_both":
        return this.previewKeepBoth(stack1, stack2, conflicts);
      case "team_vote":
        return this.previewTeamVote(stack1, stack2, conflicts);
      case "senior_override":
        return this.previewSeniorOverride(stack1, stack2, conflicts);
      case "ai_suggest":
        return this.previewAISuggest(stack1, stack2, conflicts);
      case "hybrid":
        return this.previewHybrid(stack1, stack2, conflicts);
      default:
        return {
          mergedFrames,
          keptFromStack1,
          keptFromStack2,
          conflicts,
          estimatedSuccess: 0
        };
    }
  }
  /**
   * Create a stack diff comparison
   */
  createStackDiff(baseFrameId, stack1, stack2) {
    const conflicts = this.conflictDetector.detectConflicts(stack1, stack2);
    const divergencePoint = this.findDivergencePoint(stack1, stack2);
    const commonAncestor = this.findCommonAncestor(stack1, stack2);
    const baseFrame = stack1.frames.find((f) => f.frame_id === baseFrameId) || stack2.frames.find((f) => f.frame_id === baseFrameId);
    const visualRepresentation = baseFrame ? this.visualizeDivergence(baseFrame, stack1, stack2) : void 0;
    return {
      baseFrame: baseFrameId,
      branch1: stack1,
      branch2: stack2,
      divergencePoint,
      conflicts,
      commonAncestor,
      visualRepresentation
    };
  }
  /**
   * Process a branch for visualization
   */
  processBranch(stack, parentId, xOffset, yStart) {
    const nodes = [];
    const edges = [];
    let yPos = yStart;
    const children = stack.frames.filter((f) => f.parent_frame_id === parentId);
    for (const frame of children) {
      nodes.push({
        id: frame.frame_id,
        type: xOffset < 0 ? "branch1" : "branch2",
        frame,
        position: { x: xOffset, y: yPos },
        metadata: {
          branch: xOffset < 0 ? "left" : "right",
          depth: frame.depth
        }
      });
      edges.push({
        source: parentId,
        target: frame.frame_id,
        type: "parent",
        weight: 1
      });
      const childResults = this.processBranch(
        stack,
        frame.frame_id,
        xOffset + (xOffset < 0 ? -50 : 50),
        yPos + 100
      );
      nodes.push(...childResults.nodes);
      edges.push(...childResults.edges);
      yPos += 150;
    }
    return { nodes, edges };
  }
  /**
   * Mark conflicts in the visualization
   */
  markConflicts(nodes, edges, conflicts) {
    for (const conflict of conflicts) {
      const node1 = nodes.find((n) => n.id === conflict.frameId1);
      const node2 = nodes.find((n) => n.id === conflict.frameId2);
      if (node1) {
        node1.type = "conflict";
        node1.metadata = {
          ...node1.metadata,
          conflictType: conflict.type,
          severity: conflict.severity
        };
      }
      if (node2) {
        node2.type = "conflict";
        node2.metadata = {
          ...node2.metadata,
          conflictType: conflict.type,
          severity: conflict.severity
        };
      }
      if (node1 && node2) {
        edges.push({
          source: conflict.frameId1,
          target: conflict.frameId2,
          type: "conflict",
          weight: this.getSeverityWeight(conflict.severity)
        });
      }
    }
  }
  /**
   * Get color for severity level
   */
  getSeverityColor(severity) {
    switch (severity) {
      case "critical":
        return "#ff0000";
      case "high":
        return "#ff6600";
      case "medium":
        return "#ffaa00";
      case "low":
        return "#ffdd00";
      default:
        return "#888888";
    }
  }
  /**
   * Get symbol for conflict type
   */
  getConflictSymbol(type) {
    switch (type) {
      case "parallel_solution":
        return "\u26A1";
      case "conflicting_decision":
        return "\u26A0\uFE0F";
      case "structural_divergence":
        return "\u{1F500}";
      default:
        return "\u2753";
    }
  }
  /**
   * Get weight for severity level
   */
  getSeverityWeight(severity) {
    switch (severity) {
      case "critical":
        return 4;
      case "high":
        return 3;
      case "medium":
        return 2;
      case "low":
        return 1;
      default:
        return 0;
    }
  }
  /**
   * Preview keep both strategy
   */
  previewKeepBoth(stack1, stack2, conflicts) {
    const mergedFrames = [];
    const keptFromStack1 = [];
    const keptFromStack2 = [];
    for (const frame of stack1.frames) {
      mergedFrames.push(frame);
      keptFromStack1.push(frame.frame_id);
    }
    for (const frame of stack2.frames) {
      if (!mergedFrames.find((f) => f.frame_id === frame.frame_id)) {
        mergedFrames.push(frame);
        keptFromStack2.push(frame.frame_id);
      }
    }
    const criticalConflicts = conflicts.filter(
      (c) => c.severity === "critical"
    ).length;
    const estimatedSuccess = Math.max(0, 1 - criticalConflicts * 0.2);
    return {
      mergedFrames,
      keptFromStack1,
      keptFromStack2,
      conflicts,
      estimatedSuccess
    };
  }
  /**
   * Preview team vote strategy
   */
  previewTeamVote(stack1, stack2, conflicts) {
    const mergedFrames = [];
    const keptFromStack1 = [];
    const keptFromStack2 = [];
    const conflictingFrameIds = /* @__PURE__ */ new Set();
    for (const conflict of conflicts) {
      conflictingFrameIds.add(conflict.frameId1);
      conflictingFrameIds.add(conflict.frameId2);
    }
    for (const frame of stack1.frames) {
      if (!conflictingFrameIds.has(frame.frame_id)) {
        mergedFrames.push(frame);
        keptFromStack1.push(frame.frame_id);
      }
    }
    for (const frame of stack2.frames) {
      if (!conflictingFrameIds.has(frame.frame_id) && !mergedFrames.find((f) => f.frame_id === frame.frame_id)) {
        mergedFrames.push(frame);
        keptFromStack2.push(frame.frame_id);
      }
    }
    let useStack1 = true;
    for (const conflict of conflicts) {
      if (useStack1) {
        const frame = stack1.frames.find(
          (f) => f.frame_id === conflict.frameId1
        );
        if (frame) {
          mergedFrames.push(frame);
          keptFromStack1.push(frame.frame_id);
        }
      } else {
        const frame = stack2.frames.find(
          (f) => f.frame_id === conflict.frameId2
        );
        if (frame) {
          mergedFrames.push(frame);
          keptFromStack2.push(frame.frame_id);
        }
      }
      useStack1 = !useStack1;
    }
    return {
      mergedFrames,
      keptFromStack1,
      keptFromStack2,
      conflicts,
      estimatedSuccess: 0.75
      // Team consensus usually works well
    };
  }
  /**
   * Preview senior override strategy
   */
  previewSeniorOverride(stack1, stack2, conflicts) {
    const mergedFrames = [...stack1.frames];
    const keptFromStack1 = stack1.frames.map((f) => f.frame_id);
    const keptFromStack2 = [];
    const stack1Ids = new Set(keptFromStack1);
    for (const frame of stack2.frames) {
      const hasConflict = conflicts.some((c) => c.frameId2 === frame.frame_id);
      if (!hasConflict && !stack1Ids.has(frame.frame_id)) {
        mergedFrames.push(frame);
        keptFromStack2.push(frame.frame_id);
      }
    }
    return {
      mergedFrames,
      keptFromStack1,
      keptFromStack2,
      conflicts,
      estimatedSuccess: 0.85
      // Senior override is usually reliable
    };
  }
  /**
   * Preview AI suggest strategy
   */
  previewAISuggest(stack1, stack2, conflicts) {
    const mergedFrames = [];
    const keptFromStack1 = [];
    const keptFromStack2 = [];
    for (const conflict of conflicts) {
      const frame1 = stack1.frames.find(
        (f) => f.frame_id === conflict.frameId1
      );
      const frame2 = stack2.frames.find(
        (f) => f.frame_id === conflict.frameId2
      );
      if (frame1 && frame2) {
        const score1 = this.scoreFrame(frame1);
        const score2 = this.scoreFrame(frame2);
        if (score1 >= score2) {
          mergedFrames.push(frame1);
          keptFromStack1.push(frame1.frame_id);
        } else {
          mergedFrames.push(frame2);
          keptFromStack2.push(frame2.frame_id);
        }
      }
    }
    this.addNonConflictingFrames(
      stack1,
      stack2,
      conflicts,
      mergedFrames,
      keptFromStack1,
      keptFromStack2
    );
    return {
      mergedFrames,
      keptFromStack1,
      keptFromStack2,
      conflicts,
      estimatedSuccess: 0.9
      // AI suggestions are usually optimal
    };
  }
  /**
   * Preview hybrid strategy
   */
  previewHybrid(stack1, stack2, conflicts) {
    const mergedFrames = [];
    const keptFromStack1 = [];
    const keptFromStack2 = [];
    for (const conflict of conflicts) {
      if (conflict.type === "parallel_solution") {
        const frame1 = stack1.frames.find(
          (f) => f.frame_id === conflict.frameId1
        );
        const frame2 = stack2.frames.find(
          (f) => f.frame_id === conflict.frameId2
        );
        if (frame1) {
          mergedFrames.push(frame1);
          keptFromStack1.push(frame1.frame_id);
        }
        if (frame2) {
          mergedFrames.push(frame2);
          keptFromStack2.push(frame2.frame_id);
        }
      } else if (conflict.type === "conflicting_decision") {
        const frame1 = stack1.frames.find(
          (f) => f.frame_id === conflict.frameId1
        );
        const frame2 = stack2.frames.find(
          (f) => f.frame_id === conflict.frameId2
        );
        if (frame1 && frame2) {
          const score1 = this.scoreFrame(frame1);
          const score2 = this.scoreFrame(frame2);
          if (score1 >= score2) {
            mergedFrames.push(frame1);
            keptFromStack1.push(frame1.frame_id);
          } else {
            mergedFrames.push(frame2);
            keptFromStack2.push(frame2.frame_id);
          }
        }
      } else {
        const frame1 = stack1.frames.find(
          (f) => f.frame_id === conflict.frameId1
        );
        if (frame1) {
          mergedFrames.push(frame1);
          keptFromStack1.push(frame1.frame_id);
        }
      }
    }
    this.addNonConflictingFrames(
      stack1,
      stack2,
      conflicts,
      mergedFrames,
      keptFromStack1,
      keptFromStack2
    );
    return {
      mergedFrames,
      keptFromStack1,
      keptFromStack2,
      conflicts,
      estimatedSuccess: 0.88
      // Hybrid is very effective
    };
  }
  /**
   * Score a frame for quality
   */
  scoreFrame(frame) {
    let score = 0;
    if (frame.state === "closed") score += 0.3;
    if (frame.outputs && Object.keys(frame.outputs).length > 0) score += 0.2;
    if (frame.digest_text) score += 0.2;
    if (frame.closed_at && frame.created_at) {
      const duration = frame.closed_at - frame.created_at;
      if (duration < 6e5) score += 0.3;
    }
    return score;
  }
  /**
   * Add non-conflicting frames to merge
   */
  addNonConflictingFrames(stack1, stack2, conflicts, mergedFrames, keptFromStack1, keptFromStack2) {
    const conflictingIds = /* @__PURE__ */ new Set();
    const mergedIds = new Set(mergedFrames.map((f) => f.frame_id));
    for (const conflict of conflicts) {
      conflictingIds.add(conflict.frameId1);
      conflictingIds.add(conflict.frameId2);
    }
    for (const frame of stack1.frames) {
      if (!conflictingIds.has(frame.frame_id) && !mergedIds.has(frame.frame_id)) {
        mergedFrames.push(frame);
        keptFromStack1.push(frame.frame_id);
        mergedIds.add(frame.frame_id);
      }
    }
    for (const frame of stack2.frames) {
      if (!conflictingIds.has(frame.frame_id) && !mergedIds.has(frame.frame_id)) {
        mergedFrames.push(frame);
        keptFromStack2.push(frame.frame_id);
        mergedIds.add(frame.frame_id);
      }
    }
  }
  /**
   * Find divergence point between stacks
   */
  findDivergencePoint(stack1, stack2) {
    const events1 = stack1.events.sort((a, b) => a.ts - b.ts);
    const events2 = stack2.events.sort((a, b) => a.ts - b.ts);
    for (let i = 0; i < Math.min(events1.length, events2.length); i++) {
      if (events1[i].event_id !== events2[i].event_id) {
        return events1[i].ts;
      }
    }
    return Math.min(
      events1[events1.length - 1]?.ts || 0,
      events2[events2.length - 1]?.ts || 0
    );
  }
  /**
   * Find common ancestor frame
   */
  findCommonAncestor(stack1, stack2) {
    const frames1 = new Set(stack1.frames.map((f) => f.frame_id));
    let deepestCommon;
    let maxDepth = -1;
    for (const frame of stack2.frames) {
      if (frames1.has(frame.frame_id) && frame.depth > maxDepth) {
        deepestCommon = frame;
        maxDepth = frame.depth;
      }
    }
    return deepestCommon?.frame_id;
  }
}
export {
  StackDiffVisualizer
};
//# sourceMappingURL=stack-diff.js.map
