import React, { useState } from "react";
import Viz from 'viz.js';
import { Module, render } from 'viz.js/full.render.js';
import { graphToDot } from "./Graphviz";
import * as d3 from 'd3';
import { NodeContextMenu } from "./NodeContextMenu";

const viz = new Viz({ Module, render });

export class GraphRenderer {
  graph: Graph;
  setGraph: React.Dispatch<React.SetStateAction<Graph>>;
  containerRef: React.RefObject<null>;
  contextMenu: NodeContextMenu;  

  constructor(containerRef: React.RefObject<null>) {
    [this.graph, this.setGraph] = useState(defaultGraph());
    this.containerRef = containerRef;
    this.contextMenu = new NodeContextMenu(containerRef);
  }

  public async render() {
    const dot = graphToDot(this.graph);

    try {
      const svgElement = await viz.renderSVGElement(dot, { engine: 'dot' });
      const container = this.containerRef.current;
      if (!container) return;
      container.innerHTML = '';
      container.appendChild(svgElement);
      this.attachInteractions(svgElement);
    } catch (e) {
      console.error('Viz render error', e);
    }
  }

  attachInteractions(svgElement: SVGSVGElement) {
    const svg = d3.select(svgElement);
    const self = this;
    svg.selectAll('g.node')
      .style('cursor', 'pointer')
      .on('click', function (event) {
        event.stopPropagation();
        const id = d3.select(this).attr('id');
        self.createChildNode(id);
        self.render();
      })
      .on('contextmenu', function (event) {
        event.preventDefault();
        event.stopPropagation();
        const id = d3.select(this).attr('id');
        const { clientX: x, clientY: y } = event;
        self.contextMenu.setContextMenu({ x, y, nodeId: id });
      });
  }

  createChildNode(parentId: string) {
    const id = crypto.randomUUID();
    this.setGraph(prev => ({ ...prev, nodes: [...prev.nodes, { id, label: 'New node' }], edges: [...prev.edges, { from: parentId, to: id }] }));
  }
}

function defaultGraph(): Graph {
  return {
    nodes: [
      { id: 'A', label: 'A' },
      { id: 'B', label: 'B' },
      { id: 'C', label: 'C' }
    ],
    edges: [
      { from: 'A', to: 'B' },
      { from: 'B', to: 'C' }
    ]
  };
}

export class Graph {
  nodes: Node[] = [];
  edges: Edge[] = [];
}

export class Edge {
  from: string;
  to: string;

  constructor(from: string, to: string) {
    this.from = from;
    this.to = to;
  }
}

export class Node {
  public id: string;
  public label?: string;

  constructor(id: string) {
    this.id = id;
  }
}