import json
import logging

from fasthtml.components import Script, Div

from myfasthtml.core.dbmanager import DbObject
from myfasthtml.core.instances import MultipleInstance

logger = logging.getLogger("VisNetwork")


class VisNetworkState(DbObject):
  def __init__(self, owner):
    super().__init__(owner)
    with self.initializing():
      # persisted in DB
      self.nodes: list = []
      self.edges: list = []
      self.options: dict = {
          "autoResize": True,
          "interaction": {
              "dragNodes": True,
              "zoomView": True,
              "dragView": True,
          },
          "physics": {"enabled": True}
      }


class VisNetwork(MultipleInstance):
  def __init__(self, parent, _id=None, nodes=None, edges=None, options=None):
    super().__init__(parent, _id=_id)
    logger.debug(f"VisNetwork created with id: {self._id}")
    
    self._state = VisNetworkState(self)
    self._update_state(nodes, edges, options)
  
  def _update_state(self, nodes, edges, options):
    logger.debug(f"Updating VisNetwork state with {nodes=}, {edges=}, {options=}")
    if not nodes and not edges and not options:
      return
    
    state = self._state.copy()
    if nodes is not None:
      state.nodes = nodes
    if edges is not None:
      state.edges = edges
    if options is not None:
      state.options = options
    
    self._state.update(state)
  
  def add_to_options(self, **kwargs):
    logger.debug(f"add_to_options: {kwargs=}")
    new_options = self._state.options.copy() | kwargs
    self._update_state(None, None, new_options)
    return self
  
  def render(self):
    
    # Serialize nodes and edges to JSON
    # This preserves all properties (color, shape, size, etc.) that are present
    js_nodes = ",\n    ".join(
      json.dumps(node) for node in self._state.nodes
    )
    js_edges = ",\n    ".join(
      json.dumps(edge) for edge in self._state.edges
    )
    
    # Convert Python options to JS
    js_options = json.dumps(self._state.options, indent=2)
    
    return (
        Div(
          id=self._id,
          cls="mf-vis",
        ),
        
        # The script initializing Vis.js
        Script(f"""
            (function() {{
              const container = document.getElementById("{self._id}");
              const nodes = new vis.DataSet([
                  {js_nodes}
              ]);
              const edges = new vis.DataSet([
                  {js_edges}
              ]);
              const data = {{
                  nodes: nodes,
                  edges: edges
              }};
              const options = {js_options};
              const network = new vis.Network(container, data, options);
            }})();
        """)
    )
  
  def __ft__(self):
    return self.render()
