import { devlog } from "@frontend/kitui";
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
import { Box, Button } from "@mui/material";
import {
    clearRuleForAll
} from "@root/questions/actions";
import { useQuestionsStore } from "@root/questions/store";
import { updateRootContentId } from "@root/quizes/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { cleardragQuestionContentId, setModalQuestionParentContentId, setModalQuestionTargetContentId, updateModalInfoWhyCantCreate, updateOpenedModalSettingsId } from "@root/uiTools/actions";
import { useUiTools } from "@root/uiTools/store";
import { ProblemIcon } from "@ui_kit/ProblemIcon";
import type { Core, PresetLayoutOptions, SingularData } from "cytoscape";
import Cytoscape from "cytoscape";
import popper, { getPopperInstance } from "cytoscape-popper";
import { enqueueSnackbar } from "notistack";
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
import CytoscapeComponent from "react-cytoscapejs";
import { withErrorBoundary } from "react-error-boundary";
import { DeleteNodeModal } from "../DeleteNodeModal";
import { addNode, calcNodePosition, storeToNodes } from "./helper";
import { usePopper } from "./hooks/usePopper";
import { useRemoveNode } from "./hooks/useRemoveNode";
import "./style/styles.css";
import { stylesheet } from "./style/stylesheet";

Cytoscape.use(popper);

type PopperInstance = ReturnType<getPopperInstance<SingularData>>;

function CsComponent() {
    const quiz = useCurrentQuiz();
    const desireToOpenABranchingModal = useUiTools(state => state.desireToOpenABranchingModal);
    const canCreatePublic = useUiTools(state => state.canCreatePublic);
    const modalQuestionParentContentId = useUiTools(state => state.modalQuestionParentContentId);
    const modalQuestionTargetContentId = useUiTools(state => state.modalQuestionTargetContentId);
    const trashQuestions = useQuestionsStore(state => state.questions);
    const [isPanningCy, setIsPanningCy] = useState<boolean>(false);

    const cyRef = useRef<Core | null>(null);
    const popperContainerRef = useRef<HTMLDivElement | null>(null);
    const popperInstancesRef = useRef<PopperInstance[]>([]);

    const questions = useMemo(() => trashQuestions.filter(
        (question) => question.type !== "result" && question.type !== null
    ), [trashQuestions]);

    const cyElements = useMemo(() => {
        const q = questions.filter(
            (question): question is AnyTypedQuizQuestion => question.type !== null && question.type !== "result"
        );

        return storeToNodes(q);
    }, [questions]);

    const { createPoppers, removeAllPoppers } = usePopper({
        cyRef,
        popperContainerRef,
        popperInstancesRef,
    });

    const { removeNode } = useRemoveNode({
        cyRef,
    });

    useLayoutEffect(() => {
        const cy = cyRef?.current;
        if (desireToOpenABranchingModal) {
            setTimeout(() => {
                cy?.getElementById(desireToOpenABranchingModal)?.data("eroticeyeblink", true);
            }, 250);
        } else {
            cy?.elements().data("eroticeyeblink", false);
        }
    }, [desireToOpenABranchingModal]);

    useEffect(() => {
        if (modalQuestionTargetContentId.length !== 0 && modalQuestionParentContentId.length !== 0) {
            if (!cyRef.current) return;

            addNode({
                parentNodeContentId: modalQuestionParentContentId,
                targetNodeContentId: modalQuestionTargetContentId,
            });
        }
        setModalQuestionParentContentId("");
        setModalQuestionTargetContentId("");
    }, [modalQuestionTargetContentId]);

    useEffect(function onMount() {
        updateOpenedModalSettingsId();
        document.querySelector("#root")?.addEventListener("mouseup", cleardragQuestionContentId);

        return () => {
            document.querySelector("#root")?.removeEventListener("mouseup", cleardragQuestionContentId);
            removeAllPoppers();
        };
    }, []);

    useEffect(function removePoppersOnDrag() {
        const cy = cyRef.current;
        if (!cy) return;

        let isPointerDown = false;

        const onPointerDown = () => {
            isPointerDown = true;
            cy.data("dragging", true);
        };
        const onPointerUp = () => {
            isPointerDown = false;
            cy.data("dragging", false);
            setIsPanningCy(false);
        };
        const handleMove = () => {
            setIsPanningCy(isPointerDown);
        };

        cy.on("vmousedown", onPointerDown);
        cy.on("vmousemove", handleMove);
        document.addEventListener("pointerup", onPointerUp);

        return () => {
            cy.off("vmousedown", onPointerDown);
            cy.off("vmousemove", handleMove);
            document.removeEventListener("pointerup", onPointerUp);
        };
    }, []);

    useEffect(function poppersLifecycle() {
        if (isPanningCy) {
            removeAllPoppers();
        } else {
            createPoppers();
        }
    }, [isPanningCy, createPoppers]);

    useEffect(() => {
        cyRef.current?.layout(layoutOptions).run();
        cyRef.current?.fit(undefined, 70);
        createPoppers();
    }, [cyElements, createPoppers]);

    return (
        <>
            <Box mb="20px">
                <Button
                    sx={{
                        height: "27px",
                        color: "#7E2AEA",
                        textDecoration: "underline",
                        fontSize: "16px",
                    }}
                    variant="text"
                    onClick={() => {
                        cyRef.current?.fit();
                    }}
                >
                    Выровнять
                </Button>
                <ProblemIcon
                    blink={!canCreatePublic}
                    onClick={() => updateModalInfoWhyCantCreate(true)}
                />
            </Box>

            <CytoscapeComponent
                wheelSensitivity={0.1}
                elements={cyElements}
                style={{
                    height: "480px",
                    background: "#F2F3F7",
                    overflow: "hidden",
                }}
                stylesheet={stylesheet}
                layout={layoutOptions}
                cy={(cy) => {
                    cyRef.current = cy;
                }}
                autoungrabify={true}
            />
            <DeleteNodeModal removeNode={removeNode} />
        </>
    );
}

function Clear() {
    const quiz = useCurrentQuiz();
    if (quiz) {
        updateRootContentId(quiz?.id, "");
    }
    clearRuleForAll();
    return <></>;
}

export default withErrorBoundary(CsComponent, {
    fallback: <Clear />,
    onError: (error, info) => {
        enqueueSnackbar("Дерево порвалось");
        devlog(info);
        devlog(error);
    },
});

const layoutOptions: PresetLayoutOptions = {
    name: "preset",
    positions: calcNodePosition,
    zoom: undefined,
    pan: 1,
    fit: false,
    padding: 30,
    animate: false,
    animationDuration: 500,
    animationEasing: undefined,
    animateFilter: () => false,
    ready: event => {
        if (event.cy.data("firstNode") === "nonroot") {
            event.cy.data("firstNode", "root");
            event.cy.nodes().sort((a, b) => (a.data("root") ? 1 : -1));
        } else {
            event.cy.removeData("firstNode");
        }
    },
    transform: (_, p) => p,
};
