import { useEffect, useRef, useState } from "react";
import Cytoscape from "cytoscape";
import CytoscapeComponent from "react-cytoscapejs";
import popper from "cytoscape-popper";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { updateRootInfo } from "@root/quizes/actions"
import { AnyQuizQuestion } from "@model/questionTypes/shared"
import { useQuestionsStore } from "@root/questions/store";
import { cleardragQuestionContentId, getQuestionById, updateQuestion, updateOpenedModalSettingsId, getQuestionByContentId } from "@root/questions/actions";

import { storeToNodes } from "./helper";

import "./styles.css";

import type {
  Stylesheet,
  Core,
  NodeSingular,
  AbstractEventObject,
  ElementDefinition,
} from "cytoscape";
import { QuestionsList } from "../BranchingPanel/QuestionsList";
import { enqueueSnackbar } from "notistack";

type PopperItem = {
  id: () => string;
};

type Modifier = {
  name: string;
  options: unknown;
};

type PopperConfig = {
  popper: {
    placement: string;
    modifiers?: Modifier[];
  };
  content: (items: PopperItem[]) => void;
};

type Popper = {
  update: () => Promise<void>;
  setOptions: (modifiers: { modifiers?: Modifier[] }) => void;
};

type NodeSingularWithPopper = NodeSingular & {
  popper: (config: PopperConfig) => Popper;
};

const stylesheet: Stylesheet[] = [
  {
    selector: "node",
    style: {
      shape: "round-rectangle",
      width: 130,
      height: 130,
      backgroundColor: "#FFFFFF",
      label: "data(label)",
      "font-size": "16",
      color: "#4D4D4D",
      "text-halign": "center",
      "text-valign": "center",
      "text-wrap": "wrap",
      "text-max-width": "80",
    },
  },
  {
    selector: ".multiline-auto",
    style: {
      "text-wrap": "wrap",
      "text-max-width": "80",
    },
  },
  {
    selector: "edge",
    style: {
      width: 30,
      "line-color": "#DEDFE7",
      "curve-style": "taxi",
      "taxi-direction": "horizontal",
      "taxi-turn": 60,
    },
  },
  {
    selector: ":selected",
    style: {
      "border-style": "solid",
      "border-width": 1.5,
      "border-color": "#9A9AAF",
    },
  },
];

Cytoscape.use(popper);

interface Props {
  modalQuestionParentContentId: string;
  modalQuestionTargetContentId: string;
  setOpenedModalQuestions: (open: boolean) => void;
  setModalQuestionParentContentId: (id: string) => void;
  setModalQuestionTargetContentId: (id: string) => void;
}


export const CsComponent = ({
  modalQuestionParentContentId,
  modalQuestionTargetContentId,
  setOpenedModalQuestions,
  setModalQuestionParentContentId,
  setModalQuestionTargetContentId
}: Props) => {
  const quiz = useCurrentQuiz();

  const { dragQuestionContentId, questions } = useQuestionsStore()
  const [startCreate, setStartCreate] = useState("");
  const [startRemove, setStartRemove] = useState("");

  const cyRef = useRef<Core | null>(null);
  const layoutsContainer = useRef<HTMLDivElement | null>(null);
  const plusesContainer = useRef<HTMLDivElement | null>(null);
  const crossesContainer = useRef<HTMLDivElement | null>(null);
  const gearsContainer = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    if (modalQuestionTargetContentId.length !== 0 && modalQuestionParentContentId.length !== 0) {
      console.log("был выбран вопрос " + modalQuestionTargetContentId)
      addNode({ parentNodeContentId:modalQuestionParentContentId, targetNodeContentId:modalQuestionTargetContentId })
    }

  }, [modalQuestionTargetContentId])

  const addNode = ({ parentNodeContentId, targetNodeContentId }: { parentNodeContentId: string, targetNodeContentId?: string }) => {
    console.log("dragQuestionContentId       " + dragQuestionContentId)
    const cy = cyRef?.current
    const parentNodeChildren = cy?.$('edge[source = "' + parentNodeContentId + '"]')?.length
    //если есть инфо о выбранном вопросе из модалки - берём родителя из инфо модалки. Иначе из значения дропа
    const targetQuestion = { ...getQuestionByContentId(targetNodeContentId || dragQuestionContentId) } as AnyQuizQuestion

    if (Object.keys(targetQuestion).length !== 0 && Object.keys(targetQuestion).length !== 0 && parentNodeContentId && parentNodeChildren !== undefined) {
      clearDataAfterAddNode({ parentNodeContentId, targetQuestion, parentNodeChildren })
      cy?.add([
        {
          data: {
            id: targetQuestion.content.id,
            label: targetQuestion.title || "noname"
          }
        },
        {
          data: {
            source: parentNodeContentId,
            target: targetQuestion.content.id
          }
        }
      ]).layout(lyopts).run()
    } else {
      enqueueSnackbar("Добавляемый вопрос не найден")
    }
  }

  const clearDataAfterAddNode = ({ parentNodeContentId, targetQuestion, parentNodeChildren }: { parentNodeContentId: string, targetQuestion: AnyQuizQuestion, parentNodeChildren: number }) => {
    console.log("записываю на бек ид родителя")
    console.log({ parentNodeContentId, targetQuestion, parentNodeChildren })
    //предупреждаем добавленный вопрос о том, кто его родитель
    updateQuestion(targetQuestion.content.id, question => {
      question.content.rule.parentId = parentNodeContentId
      question.content.rule.main = []
    })

    //Если детей больше 1 - предупреждаем стор вопросов об открытии модалки ветвления
    if (parentNodeChildren > 1) {
      updateOpenedModalSettingsId(parentNodeContentId)
    } else {
      //Если ребёнок первый - добавляем его родителю как дефолтный
      updateQuestion(parentNodeContentId, question => question.content.rule.default = targetQuestion.content.id)
    }
  }

  const removeNode = ({ targetNodeContentId }: { targetNodeContentId: string }) => {
      const deleteNodes = [] as string[]
      const deleteEdges:any = []
    console.log("remove")
    const cy = cyRef?.current

    const findChildrenToDelete = (node) => {

      //Узнаём грани, идущие от этой ноды
      cy?.$('edge[source = "' + node.id() + '"]')?.toArray().forEach((edge) => {
        const edgeData = edge.data()

        //записываем id грани для дальнейшего удаления
        deleteEdges.push(edge)
        //ищем ноду на конце грани, записываем её ID для дальнейшего удаления
        const targetNode = cy?.$("#" + edgeData.target)
        deleteNodes.push(targetNode.data().id)
        //вызываем функцию для анализа потомков уже у этой ноды
        findChildrenToDelete(targetNode)
      })

    }
    findChildrenToDelete(cy?.getElementById(targetNodeContentId))

    const targetQuestion = getQuestionByContentId(targetNodeContentId)

    if (targetQuestion.content.rule.parentId === "root" && quiz) {
      console.log("click ROOT")
      updateQuestion(targetNodeContentId, question => {
        question.content.rule.parentId = ""
        question.content.rule.main = []
        question.content.rule.default = ""
      })
      updateRootInfo(quiz?.id, false)
    } else {
      console.log("click  not  ROOT")
      const parentQuestionContentId = cy?.$('edge[target = "' + targetNodeContentId + '"]')?.toArray()?.[0]?.data()?.source
      if (targetNodeContentId && parentQuestionContentId) {

         clearDataAfterRemoveNode({ targetQuestionContentId: targetNodeContentId, parentQuestionContentId })
         cy?.remove(cy?.$('#' + targetNodeContentId)).layout(lyopts).run()
      }

      console.log(deleteNodes, deleteEdges)
      //После всех манипуляций удаляем грани из CS и ноды из бекенда

      deleteNodes.forEach((nodeId) => {//Ноды
        cy?.remove(cy?.$("#" + nodeId))
        updateQuestion(nodeId, question => {
          question.content.rule.parentId = ""
          question.content.rule.main = []
          question.content.rule.default = ""
        })
      })

       deleteEdges.forEach((edge:any) => {//Грани
         cy?.remove(edge)
       })

       cy?.layout(lyopts).run()

    }
  }
  const clearDataAfterRemoveNode = ({ targetQuestionContentId, parentQuestionContentId }: { targetQuestionContentId: string, parentQuestionContentId: string }) => {



    console.log({ targetQuestionContentId, parentQuestionContentId })

    updateQuestion(targetQuestionContentId, question => {
      question.content.rule.parentId = ""
      question.content.rule.main = []
      question.content.rule.default = ""
    })
    updateQuestion(parentQuestionContentId, question => {
      console.log("parent default " + question.content.rule.default)
      console.log("target ID " + targetQuestionContentId)
      console.log(question.content.rule.default === targetQuestionContentId)
      if (question.content.rule.default === targetQuestionContentId) question.content.rule.default = ""
    })

  }


  useEffect(() => {
    if (startCreate) {
      addNode({ parentNodeContentId: startCreate });
      cleardragQuestionContentId()
      setStartCreate("");
    }
  }, [startCreate]);

  useEffect(() => {
    if (startRemove) {
      removeNode({ targetNodeContentId: startRemove });
      setStartRemove("");
    }
  }, [startRemove]);


  const readyLO = (e) => {
    e.cy.data('changed', false)
    //удаляем иконки
    e.cy.nodes().forEach((ele: any) => {
      const data = ele.data()
      data.id && removeButtons(data.id);
    })
    initialPopperIcons(e)
  }

  const lyopts = {
    name: 'preset',

    positions: (e) => {
      console.log('BBBBBBBBBBBBBBB', e.cy().data('changed'))
      if (!e.cy().data('changed')) {
        console.log(e.cy().data('changed'))
        return e.data('oldPos')
      } console.log('POSITIIIIIIIONS')
      const id = e.id()
      const incomming = e.cy().edges(`[target="${id}"]`)
      const layer = 0
      e.removeData('lastChild')

      if (incomming.length === 0) {
        const children = e.cy().edges(`[source="${id}"]`)
        e.data('layer', layer)
        e.data('children', children.targets().length)
        const queue = []
        children.forEach(n => {
          queue.push({ task: n.target(), layer: layer + 1 })
        })
        while (queue.length) {
          const task = queue.pop()
          task.task.data('layer', task.layer)
          const children = e.cy().edges(`[source="${task.task.id()}"]`)
          task.task.data('children', children.targets().length)
          if (children.length !== 0) {
            children.forEach(n => queue.push({ task: n.target(), layer: task.layer + 1 }))
          }
        }
        queue.push({ parent: e, children: children.targets() })
        while (queue.length) {
          const task = queue.pop()
          if (task.children.length === 0) {
            task.parent.data('subtreeWidth', task.parent.height())
            continue
          }
          const unprocessed = task?.children.filter(e => {
            return (e.data('subtreeWidth') === undefined)
          })
          if (unprocessed.length !== 0) {
            queue.push(task)
            unprocessed.forEach(t => {
              queue.push({ parent: t, children: t.cy().edges(`[source="${t.id()}"]`).targets() })
            })
            continue
          }

          task?.parent.data('subtreeWidth', task.children.reduce((p, n) => p + n.data('subtreeWidth'), 0))
        }
        const pos = { x: 200 * e.data('layer'), y: 0 }
        e.data('oldPos', pos)
        return pos
      } else {
        const parent = e.cy().edges(`[target="${e.id()}"]`)[0].source()
        const wing = (parent.data('children') === 1) ? 0 : parent.data('subtreeWidth') / 2
        const lastOffset = parent.data('lastChild')
        const step = wing * 2 / (parent.data('children') - 1)
        //e.removeData('subtreeWidth')
        console.log('AAAAAAAAAAa', e.data(), lastOffset, step, parent.position().y, wing)
        if (lastOffset !== undefined) {
          parent.data('lastChild', lastOffset + step)
          const pos = { x: 250 * e.data('layer'), y: (lastOffset + step) }
          e.data('oldPos', pos)
          return pos
        } else {
          parent.data('lastChild', parent.position().y - wing)
          const pos = { x: 250 * e.data('layer'), y: (parent.position().y - wing) }
          e.data('oldPos', pos)
          return pos
        }
      }
    }, // map of (node id) => (position obj); or function(node){ return somPos; }
    zoom: undefined, // the zoom level to set (prob want fit = false if set)
    pan: true, // the pan level to set (prob want fit = false if set)
    fit: false, // whether to fit to viewport
    padding: 30, // padding on fit
    animate: false, // whether to transition the node positions
    animationDuration: 500, // duration of animation in ms if enabled
    animationEasing: undefined, // easing of animation if enabled
    animateFilter: function (node, i) { return true; }, // a function that determines whether the node should be animated.  All nodes animated by default on animate enabled.  Non-animated nodes are positioned immediately when the layout starts
    ready: readyLO, // callback on layoutready
    transform: function (node, position) { return position; } // transform a given node position. Useful for changing flow direction in discrete layouts
  }

  useEffect(() => {
    document.querySelector("#root")?.addEventListener("mouseup", cleardragQuestionContentId);
    const cy = cyRef.current;
    const eles = cy?.add(storeToNodes(questions))
    cy.data('changed', true)
    const elecs = eles.layout(lyopts).run()
    cy?.on('add', () => cy.data('changed', true))
    cy?.fit()
    //cy?.layout().run()

    return () => {
      document.querySelector("#root")?.removeEventListener("mouseup", cleardragQuestionContentId);
      layoutsContainer.current?.remove();
      plusesContainer.current?.remove();
      crossesContainer.current?.remove();
      gearsContainer.current?.remove();
    };
  }, []);


  const removeButtons = (id: string) => {
    layoutsContainer.current
      ?.querySelector(`.popper-layout[data-id='${id}']`)
      ?.remove();
    plusesContainer.current
      ?.querySelector(`.popper-plus[data-id='${id}']`)
      ?.remove();
    crossesContainer.current
      ?.querySelector(`.popper-cross[data-id='${id}']`)
      ?.remove();
    gearsContainer.current
      ?.querySelector(`.popper-gear[data-id='${id}']`)
      ?.remove();
  };


  const initialPopperIcons = (e) => {
    const cy = e.cy

    const container =
      (document.body.querySelector(
        ".__________cytoscape_container"
      ) as HTMLDivElement) || null;

    if (!container) {
      return;
    }

    container.style.overflow = "hidden";

    if (!plusesContainer.current) {
      plusesContainer.current = document.createElement("div");
      plusesContainer.current.setAttribute("id", "popper-pluses");
      container.append(plusesContainer.current);
    }
    if (!crossesContainer.current) {
      crossesContainer.current = document.createElement("div");
      crossesContainer.current.setAttribute("id", "popper-crosses");
      container.append(crossesContainer.current);
    }
    if (!gearsContainer.current) {
      gearsContainer.current = document.createElement("div");
      gearsContainer.current.setAttribute("id", "popper-gears");
      container.append(gearsContainer.current);
    }
    if (!layoutsContainer.current) {
      layoutsContainer.current = document.createElement("div");
      layoutsContainer.current.setAttribute("id", "popper-layouts");
      container.append(layoutsContainer.current);
    }

    const ext = cy.extent()
    const nodesInView = cy.nodes().filter(n => {
      const bb = n.boundingBox()
      return bb.x2 > ext.x1 && bb.x1 < ext.x2 && bb.y2 > ext.y1 && bb.y1 < ext.y2
    })

    nodesInView
      .toArray()
      ?.forEach((item) => {
        const node = item as NodeSingularWithPopper;

        const layoutsPopper = node.popper({
          popper: {
            placement: "left",
            modifiers: [{ name: "flip", options: { boundary: node } }],
          },
          content: ([item]) => {
            const itemId = item.id();
            const itemElement = layoutsContainer.current?.querySelector(
              `.popper-layout[data-id='${itemId}']`
            );
            if (itemElement) {
              return itemElement;
            }

            const layoutElement = document.createElement("div");
            layoutElement.style.zIndex = "0"
            layoutElement.classList.add("popper-layout");
            layoutElement.setAttribute("data-id", item.id());
            layoutElement.addEventListener("mouseup", () => {
               //Узнаём грани, идущие от этой ноды
              setModalQuestionParentContentId(item.id())
              setOpenedModalQuestions(true)
            });
            layoutsContainer.current?.appendChild(layoutElement);

            return layoutElement;
          },
        });

        const plusesPopper = node.popper({
          popper: {
            placement: "right",
            modifiers: [{ name: "flip", options: { boundary: node } }],
          },
          content: ([item]) => {
            const itemId = item.id();
            const itemElement = plusesContainer.current?.querySelector(
              `.popper-plus[data-id='${itemId}']`
            );
            if (itemElement) {
              return itemElement;
            }

            const plusElement = document.createElement("div");
            plusElement.classList.add("popper-plus");
            plusElement.setAttribute("data-id", item.id());
            plusElement.style.zIndex = "1"
            plusElement.addEventListener("mouseup", () => {
              setStartCreate(node.id());
            });
            
            plusesContainer.current?.appendChild(plusElement);

            return plusElement;
          },
        });

        const crossesPopper = node.popper({
          popper: {
            placement: "top-end",
            modifiers: [{ name: "flip", options: { boundary: node } }],
          },
          content: ([item]) => {
            const itemId = item.id();
            const itemElement = crossesContainer.current?.querySelector(
              `.popper-cross[data-id='${itemId}']`
            );
            if (itemElement) {
              return itemElement;
            }

            const crossElement = document.createElement("div");
            crossElement.classList.add("popper-cross");
            crossElement.setAttribute("data-id", item.id());
            crossElement.style.zIndex = "2"
            crossesContainer.current?.appendChild(crossElement);
            crossElement.addEventListener("mouseup", () => {
              setStartRemove(node.id())


            }
            );

            return crossElement;
          },
        });

        const gearsPopper = node.popper({
          popper: {
            placement: "left",
            modifiers: [{ name: "flip", options: { boundary: node } }],
          },
          content: ([item]) => {
            const itemId = item.id();
            if (item.cy().edges(`[target="${itemId}"]`).sources().length === 0) {
              return;
            }

            const itemElement = gearsContainer.current?.querySelector(
              `.popper-gear[data-id='${itemId}']`
            );
            if (itemElement) {
              return itemElement;
            }

            const gearElement = document.createElement("div");
            gearElement.classList.add("popper-gear");
            gearElement.setAttribute("data-id", item.id());
            gearElement.style.zIndex = "1"
            gearsContainer.current?.appendChild(gearElement);
            gearElement.addEventListener("mouseup", (e) => {
              console.log("шестерня")
              //  setOpenedModalSettings(
              //    findQuestionById(quizId, node.id().split(" ").pop() || "").index
              //  );
            });

            return gearElement;
          },
        });

        const update = async () => {
          await plusesPopper.update();
          await crossesPopper.update();
          await gearsPopper.update();
          await layoutsPopper.update();
        };

        const onZoom = (event: AbstractEventObject) => {
          const zoom = event.cy.zoom();

          update();

          crossesPopper.setOptions({
            modifiers: [
              { name: "flip", options: { boundary: node } },
              { name: "offset", options: { offset: [-5 * zoom, -30 * zoom] } },
            ],
          });

          layoutsPopper.setOptions({
            modifiers: [
              { name: "flip", options: { boundary: node } },
              { name: "offset", options: { offset: [0, -130 * zoom] } },
            ],
          });

          layoutsContainer.current
            ?.querySelectorAll("#popper-layouts > .popper-layout")
            .forEach((item) => {
              const element = item as HTMLDivElement;
              element.style.width = `${130 * zoom}px`;
              element.style.height = `${130 * zoom}px`;
            });

          plusesContainer.current
            ?.querySelectorAll("#popper-pluses > .popper-plus")
            .forEach((item) => {
              const element = item as HTMLDivElement;
              element.style.width = `${40 * zoom}px`;
              element.style.height = `${40 * zoom}px`;
              element.style.fontSize = `${40 * zoom}px`;
              element.style.borderRadius = `${6 * zoom}px`;
            });

          crossesContainer.current
            ?.querySelectorAll("#popper-crosses > .popper-cross")
            .forEach((item) => {
              const element = item as HTMLDivElement;
              element.style.width = `${24 * zoom}px`;
              element.style.height = `${24 * zoom}px`;
              element.style.fontSize = `${24 * zoom}px`;
              element.style.borderRadius = `${6 * zoom}px`;
            });

          gearsContainer.current
            ?.querySelectorAll("#popper-gears > .popper-gear")
            .forEach((item) => {
              const element = item as HTMLDivElement;
              element.style.width = `${60 * zoom}px`;
              element.style.height = `${40 * zoom}px`;
            });
        };

        node?.on("position", update);
        cy?.on("pan zoom resize render", onZoom);
      });
  };

  return (
    <>
    <CytoscapeComponent
      wheelSensitivity={0.1}
      elements={[]}
      // elements={createGraphElements(tree, quiz)}
      style={{ height: "480px", background: "#F2F3F7" }}
      stylesheet={stylesheet}
      layout={(lyopts)}
      cy={(cy) => {
        cyRef.current = cy;
      }}
    />
    <button onClick={() => {
      console.log("ELEMENTS____________________________")
      cyRef.current?.elements().forEach((ele:any) => {
        console.log(ele.data())
      })
    }}>elements</button>
    </>
  );
};
