import { ReactNode, useEffect, useRef, useState } from "react";
import { apiAuth } from "./api";
import {
  GraphCanvas,
  GraphCanvasRef,
  GraphEdge,
  GraphNode,
  SelectionProps,
  SelectionResult,
  useSelection,
} from "reagraph";
import { customTheme } from "./NetworkTheme";
import { useSession } from "./Session";
import { useNavigate } from "react-router";
import { ChevronDown, ChevronUp, Info, Settings2 } from "lucide-react";

interface NetworkData {
  nodes: GraphNode[];
  edges: GraphEdge[];
}
interface CustomSelectionProps extends SelectionProps {
  ignore: (GraphEdge | undefined)[];
}

const useCustomSelection = (props: CustomSelectionProps): SelectionResult => {
  var result = useSelection(props);
  result.actives = result.actives.filter(
    (s) => !props.ignore.map((edge) => edge?.id).includes(s)
  );
  const ignored_nodes = props.ignore.map((edge) =>
    edge &&
    result.selections?.includes(edge.source) &&
    !result.selections?.includes(edge.target)
      ? edge.target
      : ""
  );
  result.actives = result.actives.filter((s) => !ignored_nodes.includes(s));
  return result;
};

export const GraphComponent = () => {
  let initialData = { nodes: [], edges: [] } as NetworkData;
  const [data, setData] = useState(initialData);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");
  const [threed, setThreed] = useState(false);
  const [showControls, setShowControls] = useState(true);
  const [likes, setLikes] = useState(2);
  const [showLikes, setShowLikes] = useState(true);
  const [showDislikes, setShowDislikes] = useState(false);
  const [popularity, setPopularity] = useState(false);
  const [mutuality, setMutuality] = useState(false);
  const [showHelp, setShowHelp] = useState(false);
  const { user, teams } = useSession();
  const navigate = useNavigate();
  useEffect(() => {
    user?.scopes.includes(`team:${teams?.activeTeam}`) ||
      teams?.activeTeam === 42 ||
      navigate("/", { replace: true });
  }, [user]);

  async function loadData() {
    setLoading(true);
    if (teams) {
      await apiAuth(`analysis/graph_json/${teams?.activeTeam}`, null)
        .then((data) => {
          if (data.detail) {
            setError(data.detail);
            return initialData;
          } else {
            setError("");
            return data as Promise<NetworkData>;
          }
        })
        .then((data) => setData(data))
        .catch(() => setError("no access"));
      setLoading(false);
    } else setError("team unknown");
  }

  useEffect(() => {
    loadData();
  }, [teams]);

  const graphRef = useRef<GraphCanvasRef | null>(null);

  function handleThreed() {
    setThreed(!threed);
    graphRef.current?.fitNodesInView();
    graphRef.current?.centerGraph();
    graphRef.current?.resetControls();
  }

  function handlePopularity() {
    popularityLabel(!popularity);
    setPopularity(!popularity);
  }

  function handleMutuality() {
    colorMatches(!mutuality);
    setMutuality(!mutuality);
  }

  function findMatches(edges: GraphEdge[]) {
    const adjacencyList = edges.map(
      (edge) => edge.source + edge.target + edge.data.relation
    );
    return edges.filter((edge) =>
      adjacencyList.includes(edge.target + edge.source + edge.data.relation)
    );
  }
  //const matches = useMemo(() => findMatches(data.edges), [])

  function colorMatches(mutuality: boolean) {
    const matches = findMatches(data.edges);
    const newEdges = data.edges;
    if (mutuality) {
      newEdges.forEach((edge) => {
        if (
          (likes === 1 || edge.data.relation === likes) &&
          matches.map((edge) => edge.id).includes(edge.id)
        ) {
          edge.fill = "#9c3";
          if (edge.size) edge.size = edge.size * 1.5;
        }
      });
    } else {
      newEdges.forEach((edge) => {
        if (
          (likes === 1 || edge.data.relation === likes) &&
          matches.map((edge) => edge.id).includes(edge.id)
        ) {
          edge.fill = edge.data.origFill;
          edge.size = edge.data.origSize;
        }
      });
    }
    setData({ nodes: data.nodes, edges: newEdges });
  }

  function popularityLabel(popularity: boolean) {
    const newNodes = data.nodes;
    console.log(data.nodes);
    if (popularity) {
      newNodes.forEach(
        (node) => (node.subLabel = `pop.: ${node.data.inDegree.toFixed(1)}`)
      );
    } else {
      newNodes.forEach((node) => (node.subLabel = undefined));
    }
    setData({ nodes: newNodes, edges: data.edges });
  }

  useEffect(() => {
    setLikes(+showLikes + +!showDislikes);
  }, [showLikes, showDislikes]);

  useEffect(() => {
    if (mutuality) colorMatches(false);
    colorMatches(mutuality);
  }, [likes]);

  const {
    selections,
    actives,
    onNodeClick,
    onCanvasClick,
    onNodePointerOver,
    onNodePointerOut,
  } = useCustomSelection({
    ref: graphRef,
    nodes: data.nodes,
    edges: data.edges.filter((edge) => edge.data.relation === likes),
    ignore: data.edges.map((edge) => {
      if (likes === 1 && edge.data.relation !== 2) return edge;
    }),
    pathSelectionType: "out",
    pathHoverType: "in",
    type: "multiModifier",
  });

  let content: ReactNode;
  if (loading) {
    <div className="container">
      <progress className="progress is-primary" max="100"></progress>
    </div>;
  } else if (error) {
    content = <span>{error}</span>;
  } else {
    content = (
      <>
        <GraphCanvas
          draggable
          cameraMode={threed ? "rotate" : "pan"}
          layoutType={threed ? "forceDirected3d" : "forceDirected2d"}
          layoutOverrides={{
            nodeStrength: -200,
            linkDistance: 100,
          }}
          labelType="nodes"
          sizingType="attribute"
          sizingAttribute={popularity ? "inDegree" : undefined}
          ref={graphRef}
          theme={customTheme}
          nodes={data.nodes}
          edges={data.edges.filter(
            (edge) => edge.data.relation === likes || likes === 1
          )}
          selections={selections}
          actives={actives}
          onCanvasClick={onCanvasClick}
          onNodeClick={onNodeClick}
          onNodePointerOut={onNodePointerOut}
          onNodePointerOver={onNodePointerOver}
        />
      </>
    );
  }

  return (
    <div>
      <div className="card network-controls">
        <header
          className="card-header"
          style={{ cursor: "pointer" }}
          onClick={() => setShowControls(!showControls)}
        >
          <p className="card-header-title">
            <span className="icon-text">
              <span className="icon">
                <Settings2 />
              </span>
              <span>controls</span>
            </span>
          </p>
          <button className="card-header-icon">
            {showControls ? <ChevronUp /> : <ChevronDown />}
          </button>
        </header>
        {showControls && (
          <div className="card-content">
            <div className="field">
              <div className="field is-grouped">
                <div className="control">
                  <button
                    className={
                      "button is-primary" + (showLikes ? "" : " is-outlined")
                    }
                    onClick={() => setShowLikes(!showLikes)}
                  >
                    <span className="icon">👍</span>
                  </button>
                </div>
                <div className="control">
                  <button
                    className={
                      "button is-primary" + (showDislikes ? "" : " is-outlined")
                    }
                    onClick={() => setShowDislikes(!showDislikes)}
                  >
                    <span className="icon">👎</span>
                  </button>
                </div>
              </div>

              <div className="control">
                <label className="checkbox">
                  <input
                    type="checkbox"
                    checked={mutuality}
                    onClick={handleMutuality}
                  />
                  <span className="ml-1">mutuality</span>
                </label>
              </div>

              <div className="control">
                <label className="radio">
                  <input
                    type="radio"
                    checked={!threed}
                    name="3D"
                    onClick={handleThreed}
                  />
                  <span className="m-1">2D</span>
                </label>
                <label className="radio">
                  <input
                    type="radio"
                    checked={threed}
                    name="3D"
                    onClick={handleThreed}
                  />
                  <span className="m-1">3D</span>
                </label>
              </div>

              <div className="control">
                <label className="checkbox">
                  <input
                    type="checkbox"
                    checked={popularity}
                    onClick={handlePopularity}
                  />
                  <span className="ml-1">popularity</span>
                </label>
              </div>
            </div>
          </div>
        )}
        {showControls && (
          <footer className="card-footer">
            <button
              onClick={() => {
                setShowHelp(true);
              }}
              className="card-footer-item"
            >
              <p className="icon-text is-small">
                <span className="icon">
                  <Info />
                </span>
                <span>help</span>
              </p>
            </button>
          </footer>
        )}
      </div>

      <div className={"modal" + (showHelp ? " is-active" : "")}>
        <div
          onClick={() => setShowHelp(false)}
          className="modal-background"
        ></div>
        <div className="modal-content">
          <article className="message is-info">
            <div className="message-header">
              <p>Help</p>
              <button
                onClick={() => setShowHelp(false)}
                className="delete"
                aria-label="delete"
              ></button>
            </div>
            <div className="message-body">
              <p>scroll to zoom</p>
              <p>
                <strong>hover</strong>: show inbound links
              </p>
              <p>
                <strong>click</strong>: show outward links
              </p>
              <hr className="has-background-info" />
              <p>
                multi-selection is possible with
                <br />
                <i>Ctrl</i> or <i>Shift</i>
              </p>
              <br />
              <p>drag to pan/rotate</p>
              <hr className="has-background-info" />
              <p>popularity is meassured by rank-weighted in-degree</p>
            </div>
          </article>
        </div>
      </div>

      {content}
    </div>
  );
};
