// utils/enhancedGraphUtils.ts

import { Neo4jGraphData, D3Node, D3Link } from '../graphTypes/graphTypes';

interface NodePosition {
  id: string;
  x: number;
  y: number;
}

// Store node positions to maintain stability across updates
const nodePositions = new Map<string, NodePosition>();

export const prepareGraphDataWithStablePositions = (
  graphData: Neo4jGraphData,
  canvasWidth: number = 800,
  canvasHeight: number = 600,
  newNodePosition?: { x: number; y: number }
): { nodes: D3Node[]; links: D3Link[] } => {
  const centerX = canvasWidth / 2;
  const centerY = canvasHeight / 2;

  // Convert nodes to D3 format with stable positioning
  const nodes: D3Node[] = graphData.nodes.map((node, index) => {
    let x, y;
    
    // Check if we have a stored position for this node
    const storedPosition = nodePositions.get(node.id);
    
    if (storedPosition) {
      x = storedPosition.x;
      y = storedPosition.y;
    } else {
      // This is a new node - assign initial position
      if (graphData.nodes.length === 1) {
        // Single node goes to center
        x = centerX;
        y = centerY;
      } else if (index === graphData.nodes.length - 1 && newNodePosition) {
        // This is the newest node and we have a specific position
        x = newNodePosition.x;
        y = newNodePosition.y;
      } else {
        // Place near existing nodes in a loose spiral pattern
        const existingPositions = Array.from(nodePositions.values());
        if (existingPositions.length > 0) {
          // Find the center of existing nodes
          const avgX = existingPositions.reduce((sum, pos) => sum + pos.x, centerX) / (existingPositions.length + 1);
          const avgY = existingPositions.reduce((sum, pos) => sum + pos.y, centerY) / (existingPositions.length + 1);
          
          // Place new node in a circle around the average position
          const angle = (index * 2 * Math.PI) / Math.max(graphData.nodes.length, 3);
          const radius = 100 + (index * 20); // Expanding spiral
          x = avgX + Math.cos(angle) * radius;
          y = avgY + Math.sin(angle) * radius;
        } else {
          // Fallback to center with slight offset
          const angle = (index * 2 * Math.PI) / graphData.nodes.length;
          x = centerX + Math.cos(angle) * 80;
          y = centerY + Math.sin(angle) * 80;
        }
      }
      
      // Store the position
      nodePositions.set(node.id, { id: node.id, x, y });
    }

    return {
      id: node.id,
      labels: node.labels,
      properties: node.properties,
      x,
      y
    };
  });

  // Remove positions for nodes that no longer exist
  const currentNodeIds = new Set(graphData.nodes.map(n => n.id));
  for (const [id] of nodePositions) {
    if (!currentNodeIds.has(id)) {
      nodePositions.delete(id);
    }
  }

  // Convert relationships to D3 format
  const links: D3Link[] = graphData.relationships.map(rel => ({
    source: rel.startNodeId,
    target: rel.endNodeId,
    id: rel.id,
    type: rel.type,
    properties: rel.properties,
    startNodeId: rel.startNodeId,
    endNodeId: rel.endNodeId
  }));

  return { nodes, links };
};

// Update stored position when node is dragged
export const updateNodePosition = (nodeId: string, x: number, y: number): void => {
  nodePositions.set(nodeId, { id: nodeId, x, y });
};

// Clear all stored positions (useful for reset)
export const clearStoredPositions = (): void => {
  nodePositions.clear();
};

// Get current stored positions (useful for debugging)
export const getStoredPositions = (): Map<string, NodePosition> => {
  return new Map(nodePositions);
};

// Force layout configuration for different graph sizes
export const getOptimalForceConfig = (nodeCount: number, relationshipCount: number) => {
  const density = relationshipCount / Math.max(nodeCount, 1);
  
  return {
    linkDistance: Math.max(80, Math.min(200, 120 + (density * 50))),
    chargeStrength: Math.max(-800, -200 - (nodeCount * 10)),
    collisionRadius: Math.max(30, 25 + (density * 10)),
    centerStrength: Math.max(0.05, Math.min(0.3, 0.1 + (1 / nodeCount)))
  };
};