import os
import streamlit.components.v1 as components
from neo4j.graph import Graph
import datetime

_RELEASE = True

# Declare a Streamlit component. `declare_component` returns a function
# that is used to create instances of the component. We're naming this
# function "_component_func", with an underscore prefix, because we don't want
# to expose it directly to users. Instead, we will create a custom wrapper
# function, below, that will serve as our component's public API.

# It's worth noting that this call to `declare_component` is the
# *only thing* you need to do to create the binding between Streamlit and
# your component frontend. Everything else we do in this file is simply a
# best practice.

if not _RELEASE:
    _component_func = components.declare_component(
        # We give the component a simple, descriptive name ("my_component"
        # does not fit this bill, so please choose something better for your
        # own component :)
        "st_sigmagraph",
        # Pass `url` here to tell Streamlit that the component will be served
        # by the local dev server that you run via `npm run start`.
        # (This is useful while your component is in development.)
        url="http://localhost:3001",
    )
else:
    # When we're distributing a production version of the component, we'll
    # replace the `url` param with `path`, and point it to the component's
    # build directory:
    parent_dir = os.path.dirname(os.path.abspath(__file__))
    build_dir = os.path.join(parent_dir, "frontend/build")
    _component_func = components.declare_component("", path=build_dir)


def serialize_neo4j_value(val):
    import math
    import numpy as np
    # Neo4j Date/Time types
    if hasattr(val, 'isoformat'):
        return val.isoformat()
    if isinstance(val, (float, np.floating)) and (math.isnan(val) or math.isinf(val)):
        return None
    if val is None or val is np.nan:
        return None
    if isinstance(val, (list, tuple)):
        return [serialize_neo4j_value(v) for v in val]
    if isinstance(val, dict):
        return {k: serialize_neo4j_value(v) for k, v in val.items()}
    return val



def neo4jgraph_to_sigma(result):      
        # extract nodes and relationships
        nodes = []
        relationships = []
                
        
        for node in result.nodes:
            nodes.append({
                "identity": node.element_id,
                "labels": list(node.labels),
                "properties": {k: serialize_neo4j_value(v) for k, v in dict(node).items()}
            })
                       
        for rel in result.relationships:
            relationships.append({
                "identity": rel.element_id,
                "start": rel.start_node.element_id,
                "end": rel.end_node.element_id,
                "type": rel.type,
                "properties": {k: serialize_neo4j_value(v) for k, v in dict(rel).items()}
            })
        
        return {
            "nodes": nodes,
            "relationships": relationships
        }

# Create a wrapper function for the component. This is an optional
# best practice - we could simply expose the component function returned by
# `declare_component` and call it done. The wrapper allows us to customize
# our component's API: we can pre-process its input args, post-process its
# output value, and add a docstring for users.
def st_sigmagraph(graphData=None, height=600, key=None):
    """Create a new instance of "my_component".

    Parameters
    ----------
    name: str
        The name of the thing we're saying hello to. The component will display
        the text "Hello, {name}!"
    key: str or None
        An optional key that uniquely identifies this component. If this is
        None, and the component's arguments are changed, the component will
        be re-mounted in the Streamlit frontend and lose its current state.

    Returns
    -------
    int
        The number of times the component's "Click Me" button has been clicked.
        (This is the value passed to `Streamlit.setComponentValue` on the
        frontend.)

    """
    # Call through to our private component function. Arguments we pass here
    # will be sent to the frontend, where they'll be available in an "args"
    # dictionary.
    #
    # "default" is a special argument that specifies the initial return
    # value of the component before the user has interacted with it.
    

    component_value = _component_func(
            graphData=graphData,
            height=height,
            key=key,
            default=None
        )
    
    return component_value
