import { CosmosInputLink, CosmosInputNode } from '@cosmograph/cosmos';
import { Cosmograph, CosmographProvider, CosmographRef } from '@cosmograph/react';
import { scaleSymlog } from 'd3-scale';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Button } from 'src/components';
import useAdviserTexts from 'src/features/adviser/useAdviserTexts';
import useGraphLimit from 'src/pages/robot/Brain/useGraphLimit';
import { isDevEnv } from 'src/utils/dev';
import GraphHoverInfo from '../CyberlinksGraph/GraphHoverInfo/GraphHoverInfo';
import GraphActionBar from '../graph/GraphActionBar/GraphActionBar';
import { useCyberlinkWithWaitAndAdviser } from '../hooks/useCyberlink';
import styles from './GraphNew.module.scss';

function GraphNew({ address, data, size }) {
  const cosmograph = useRef<CosmographRef>();
  const [_degree, setDegree] = useState<number[]>([]);
  // const histogram = useRef<CosmographHistogramRef<Node>>();
  // const timeline = useRef<CosmographTimelineRef<Link>>();
  // const search = useRef<CosmographSearchRef>();

  const { limit, isCurvedStyle } = useGraphLimit();

  const [hoverNode, setHoverNode] = useState<CosmosInputNode>();
  const [selectedNodes, setSelectedNodes] = useState<CosmosInputNode[]>([]);

  const [localData, setLocalData] = useState<{
    nodes: CosmosInputNode[];
    links: CosmosInputLink[];
  }>({
    links: [],
    nodes: [],
  });

  // sync with lib state
  // was issue with order after selecting nodes, if use only it
  useEffect(() => {
    cosmograph.current?.selectNodes(selectedNodes);
  }, [selectedNodes]);

  useEffect(() => {
    const timeoutId = setTimeout(() => {
      cosmograph.current?.pause();
    }, 4500);
    return () => clearTimeout(timeoutId);
  }, []);

  const scaleColor = useRef(
    scaleSymlog<string, string>()
      .range(['rgba(80, 105, 180, 0.75)', 'rgba(240, 105, 180, 0.75)'])
      .clamp(true)
  );

  useEffect(() => {
    const degree = cosmograph?.current?.getNodeDegrees();
    if (degree) {
      scaleColor.current.domain([Math.min(...degree), Math.max(...degree)]);
      setDegree(degree);
    }
  }, []);

  const handleNodeSelection = useCallback(
    (node?: CosmosInputNode) => {
      if (!node) {
        setSelectedNodes([]);
        return;
      }

      let newNodes = [...selectedNodes];

      // check for duplicate
      if (newNodes.find((n) => n.id === node.id)) {
        newNodes = newNodes.filter((n) => n.id !== node.id);
      } else if (newNodes.length < 2) {
        newNodes.push(node);
      } else {
        newNodes = [node];
      }

      setSelectedNodes(newNodes);
    },
    [selectedNodes]
  );

  function callback() {
    setLocalData({
      ...localData,

      links: [
        ...localData.links,
        {
          source: selectedNodes[0].id,
          target: selectedNodes[1].id,
          color: 'red',
        },
      ],
    });

    cosmograph.current?.unselectNodes();
  }

  const { links, nodes } = useMemo(() => {
    const nodes = [...data.nodes, ...localData.nodes].map((node) => {
      return {
        ...node,
        size: 0.5,
        // value: 1,
        color: node.color || 'rgba(0,100,235,1)',
      };
    });

    const links = [...data.links, ...localData.links].map((link) => {
      return {
        ...link,
        width: 3.5,
        color: link.color || 'rgba(9,255,13,1)',
      };
    });

    return { links, nodes };
  }, [data, localData]);

  useAdviserTexts({
    defaultText: useMemo(() => {
      return (
        <>
          {/* @nick (or) your */}
          public brain, with {nodes.length} particles and {links.length} cyberlinks
          <br />
          The limit is {limit}
        </>
      );
    }, [nodes.length, links.length, limit]),
  });

  function renderInfo(node, position?: string) {
    const xOffset = 100;
    const toRight = position === 'right';

    return (
      <GraphHoverInfo
        node={node}
        left={!toRight ? xOffset : undefined}
        right={toRight ? xOffset : undefined}
        top="42.5vh"
        size={size || window.innerWidth}
      />
    );
  }

  const selectedNodeIds = selectedNodes.map((n) => n.id);

  return (
    <div className={styles.wrapper}>
      {/* complex checks, change carefully */}
      {(selectedNodes[0] || hoverNode) &&
        renderInfo(
          (hoverNode &&
          // (selectedNodes[1] && selectedNodes[1].id !== hoverNode.id))
          selectedNodes.length === 0
            ? hoverNode
            : false) || selectedNodes[0]
        )}

      {(selectedNodes[1] || (hoverNode && selectedNodes.length === 1)) &&
        renderInfo(
          (hoverNode &&
          // render right only if first node selected
          selectedNodes.length === 1 &&
          selectedNodes[0].id !== hoverNode.id
            ? hoverNode
            : false) || selectedNodes[1],
          'right'
        )}

      <CosmographProvider nodes={nodes} links={links}>
        {/* <CosmographSearch
          ref={search}
          className="searchStyle"
          onSelectResult={onSearchSelectResult}
          maxVisibleItems={20}
        /> */}
        {nodes.length > 0 && (
          <Cosmograph
            ref={cosmograph}
            className={styles.cosmographStyle}
            // spaceSize={size}
            // showTopLabelsLimit={10}
            // showTopLabels={}
            backgroundColor="transparent"
            showDynamicLabels={false}
            linkArrows={false}
            linkWidth={2}
            focusedNodeRingColor="rgba(243, 30, 30, 0.5)"
            curvedLinks={isCurvedStyle}
            showHoveredNodeLabel={false}
            nodeLabelColor="white"
            simulationFriction={0.95}
            simulationDecay={5000}
            nodeGreyoutOpacity={0.35}
            linkGreyoutOpacity={0.35}
            hoveredNodeLabelColor="white"
            nodeSize={(n) => n.size ?? null}
            nodeColor={(d) => {
              return selectedNodeIds.includes(d.id) ? 'rgb(246, 43, 253)' : d.color;
            }}
            linkColor={(d) => d.color}
            // linkWidth={(l: Link) => l.width ?? null}
            // linkColor={(l: Link) => l.color ?? null}

            onClick={(node) => {
              handleNodeSelection(node);

              if (!cosmograph.current?.isSimulationRunning && !node) {
                cosmograph.current?.restart();
              } else {
                cosmograph.current?.pause();
              }
            }}
            onNodeMouseOver={(n) => {
              cosmograph.current?.pause();
              setHoverNode(n);
            }}
            onNodeMouseOut={() => {
              setHoverNode(undefined);
            }}
            showFPSMonitor={isDevEnv()}
          />
        )}
      </CosmographProvider>

      <GraphActionBar>
        <CyberlinkButton selectedNodes={selectedNodes} callback={callback} />
      </GraphActionBar>
    </div>
  );
}

export default GraphNew;

type Props2 = {
  selectedNodes: CosmosInputNode[];
  callback: () => void;
};

// todo:
// pass only cids
// check if have energy to link
function CyberlinkButton({ selectedNodes, callback }: Props2) {
  const { isReady, isLoading, execute } = useCyberlinkWithWaitAndAdviser({
    to: selectedNodes[0]?.id,
    from: selectedNodes[1]?.id,
    callback,
  });

  const { length } = selectedNodes;

  let text;
  if (length !== 2 || length === 0) {
    text = `select ${2 - length}  particle${length === 0 ? 's' : ''}`;
  } else {
    text = 'cyberlink particles';
  }

  return (
    <Button onClick={execute} disabled={!isReady || selectedNodes.length !== 2} pending={isLoading}>
      {text}
    </Button>
  );
}

Neighbours