import { CONSTANTS } from "./constant";
import DragManager from "./DragManager";
import * as d3 from 'd3';
import { 
  Neo4jNode, 
  D3Node, 
  D3Link, 
} from '../../graphTypes/graphTypes';
import { 
  getNodeColor, 
  getNodeDisplayName, 
  truncateText 
} from '../../graphUtils/graphUtils';
import { defaultGraphConfig, graphStyles } from '../../graphConfig/graphConfig';
import ColorManager from "./colorManager";


class NodeManager {
  static createNodes(
    container: d3.Selection<SVGGElement, unknown, null, undefined>,
    svg: d3.Selection<SVGSVGElement, unknown, null, undefined>,
    nodes: D3Node[],
    nodeRadius: number,
    simulation: d3.Simulation<D3Node, D3Link>,
    onNodeClick: (node: Neo4jNode | null) => void,
    onNodeDoubleClick: ((node: Neo4jNode) => void) | undefined,
    selectedNode: Neo4jNode | null,
    isAddingNode: boolean
  ) {
    const nodeElements = container.selectAll('.node')
      .data(nodes)
      .enter().append('circle')
      .attr('class', 'node')
      .attr('r', nodeRadius)
      .attr('fill', d => {
        const labelColor = ColorManager.getEnhancedNodeColor(d);
        return ColorManager.createGradient(svg, labelColor, d.id);
      })
      .attr('stroke', CONSTANTS.COLORS.NODE_STROKE)
      .attr('stroke-width', CONSTANTS.SIZES.STROKE_WIDTH.default)
      .attr('opacity', 0.9);

    // Add event handlers
    this.addNodeEventHandlers(
      nodeElements, 
      svg, 
      onNodeClick, 
      onNodeDoubleClick, 
      selectedNode, 
      isAddingNode
    );

    // Enable dragging
    DragManager.enableNodeDragging(nodeElements, simulation, isAddingNode);
    DragManager.updateCursorForDragging(nodeElements, isAddingNode);

    return nodeElements;
  }

  static createNodeLabels(
    container: d3.Selection<SVGGElement, unknown, null, undefined>,
    nodes: D3Node[]
  ) {
    return container.selectAll('.node-label')
      .data(nodes)
      .enter().append('text')
      .attr('class', 'node-label')
      .attr('text-anchor', 'middle')
      .attr('dy', '.35em')
      .attr('font-size', graphStyles.text.fontSize)
      .attr('font-family', graphStyles.text.fontFamily)
      .attr('font-weight', graphStyles.text.fontWeight)
      .attr('fill', CONSTANTS.COLORS.TEXT_NODE)
      .attr('pointer-events', 'none')
      .text(d => {
        const displayName = truncateText(getNodeDisplayName(d), 10);
        const level = this.getNodeLevel(d);
        return level ? `${displayName} (${level})` : displayName;
      });
  }

  static createLevelIndicators(
    container: d3.Selection<SVGGElement, unknown, null, undefined>,
    nodes: D3Node[]
  ) {
    const nodesWithLevels = nodes.filter(d => this.getNodeLevel(d));
    
    return container.selectAll('.level-indicator')
      .data(nodesWithLevels)
      .enter().append('circle')
      .attr('class', 'level-indicator')
      .attr('r', CONSTANTS.SIZES.LEVEL_INDICATOR_RADIUS)
      .attr('fill', d => ColorManager.getEnhancedNodeColor(d))
      .attr('stroke', CONSTANTS.COLORS.NODE_STROKE)
      .attr('stroke-width', 1)
      .attr('pointer-events', 'none');
  }

  private static getNodeLevel(node: D3Node): string | null {
    const level = node.properties?.level || 
                 node.properties?.levelName || 
                 node.properties?.priority ||
                 node.properties?.rank ||
                 node.properties?.category ||
                 node.properties?.status;
    
    return (level && level !== 'default' && level !== 'undefined') ? level : null;
  }

  private static addNodeEventHandlers(
    nodeElements: d3.Selection<SVGCircleElement, D3Node, SVGGElement, unknown>,
    svg: d3.Selection<SVGSVGElement, unknown, null, undefined>,
    onNodeClick: (node: Neo4jNode | null) => void,
    onNodeDoubleClick: ((node: Neo4jNode) => void) | undefined,
    selectedNode: Neo4jNode | null,
    isAddingNode: boolean
  ): void {
    nodeElements
      .on('click', (event, d) => {
        if (!isAddingNode) {
          event.stopPropagation();
          onNodeClick(d);
        }
      })
      .on('dblclick', (event, d) => {
        if (!isAddingNode && onNodeDoubleClick) {
          event.stopPropagation();
          onNodeDoubleClick(d);
        }
      })
      .on('mouseover', function(event, d) {
        if (!isAddingNode && selectedNode?.id !== d.id) {
          const labelColor = ColorManager.getEnhancedNodeColor(d);
          d3.select(this)
            .attr('stroke', CONSTANTS.COLORS.HOVER)
            .attr('stroke-width', CONSTANTS.SIZES.STROKE_WIDTH.hover)
            .attr('filter', 'url(#glow)')
            .attr('opacity', 1.0)
            .attr('fill', d3.rgb(labelColor).brighter(0.3).toString());
        }
      })
      .on('mouseout', function(event, d) {
        if (!isAddingNode && selectedNode?.id !== d.id) {
          const labelColor = ColorManager.getEnhancedNodeColor(d);
          d3.select(this)
            .attr('stroke', CONSTANTS.COLORS.NODE_STROKE)
            .attr('stroke-width', CONSTANTS.SIZES.STROKE_WIDTH.default)
            .attr('filter', null)
            .attr('opacity', 0.9)
            .attr('fill', ColorManager.createGradient(svg, labelColor, d.id));
        }
      });
  }
}

export default NodeManager;