import { jsx, jsxs } from "react/jsx-runtime";
import { Panel, applyEdgeChanges, applyNodeChanges } from "../../xyflow/react.js";
import { Spinner } from "@uipath/apollo-wind";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { PREVIEW_NODE_ID } from "../../constants.js";
import { Breadcrumb } from "../../controls/index.js";
import { useNodeManifests } from "../../core/index.js";
import { useAddNodeOnConnectEnd } from "../../hooks/useAddNodeOnConnectEnd.js";
import { useCanvasEvent } from "../../hooks/useCanvasEvents.js";
import { useCanvasNodeLayout } from "../../hooks/useCanvasNodeLayout.js";
import { animatedViewportManager } from "../../stores/animatedViewportManager.js";
import { selectBreadcrumbs, selectCanvasStack, selectCurrentCanvas, selectCurrentPath, selectDrillIntoNode, selectInitializeCanvas, selectInitializeWithData, selectNavigateToDepth, selectNavigateToSiblingCanvas, selectPreviousCanvas, selectTransitionState, selectUpdateEdges, selectUpdateNodes, selectUpdateSelection, selectUpdateViewport, useCanvasStore } from "../../stores/canvasStore.js";
import { viewportManager } from "../../stores/viewportManager.js";
import { DefaultCanvasTranslations } from "../../types.js";
import { isContainerNodeManifest } from "../../utils/index.js";
import { isPreviewEdge } from "../../utils/createPreviewNode.js";
import { CanvasIcon } from "../../utils/icon-registry.js";
import { prefersReducedMotion } from "../../utils/transitions.js";
import { AddNodeManager } from "../AddNodePanel/AddNodeManager.js";
import { AddNodePreview } from "../AddNodePanel/AddNodePreview.js";
import { createAddNodePreview } from "../AddNodePanel/createAddNodePreview.js";
import { BaseCanvas } from "../BaseCanvas/index.js";
import { BaseNode } from "../BaseNode/index.js";
import { BlankCanvasNode } from "../BlankCanvasNode/index.js";
import { CanvasPositionControls } from "../CanvasPositionControls.js";
import { LoopCanvasNode } from "../LoopNode/index.js";
import { MiniCanvasNavigator } from "../MiniCanvasNavigator/index.js";
const DEFAULT_NODE_TYPES = {
    default: BaseNode,
    'blank-canvas-node': BlankCanvasNode,
    preview: AddNodePreview
};
function shouldPersistNodeChange(change) {
    if ('position' === change.type) return !!change.dragging;
    if ('dimensions' === change.type) return !!change.setAttributes;
    return true;
}
function isDefaultViewport(viewport) {
    return 0 === viewport.x && 0 === viewport.y && 1 === viewport.zoom;
}
const HierarchicalCanvas = ({ mode = 'design', initialCanvases, initialPath, onCanvasesChange, onPathChange })=>{
    const canvasRef = useRef(null);
    const containerRef = useRef(null);
    const [reactFlowInstance, setReactFlowInstance] = useState(null);
    const currentViewportRef = useRef({
        x: 0,
        y: 0,
        zoom: 1
    });
    const lastCanvasIdRef = useRef(null);
    const shouldAnimate = 'design' === mode && !prefersReducedMotion();
    const nodeManifests = useNodeManifests();
    const nodeTypes = useMemo(()=>nodeManifests.reduce((acc, manifest)=>{
            acc[manifest.nodeType] = isContainerNodeManifest(manifest) ? LoopCanvasNode : BaseNode;
            return acc;
        }, {
            ...DEFAULT_NODE_TYPES
        }), [
        nodeManifests
    ]);
    const { getManifestForNode } = useCanvasNodeLayout();
    const currentCanvas = useCanvasStore(selectCurrentCanvas);
    const previousCanvas = useCanvasStore(selectPreviousCanvas);
    const breadcrumbs = useCanvasStore(selectBreadcrumbs);
    const transitionState = useCanvasStore(selectTransitionState);
    const canvasStack = useCanvasStore(selectCanvasStack);
    const currentPath = useCanvasStore(selectCurrentPath);
    const initializeCanvas = useCanvasStore(selectInitializeCanvas);
    const initializeWithData = useCanvasStore(selectInitializeWithData);
    const navigateToDepth = useCanvasStore(selectNavigateToDepth);
    const navigateToSiblingCanvas = useCanvasStore(selectNavigateToSiblingCanvas);
    const updateNodes = useCanvasStore(selectUpdateNodes);
    const updateEdges = useCanvasStore(selectUpdateEdges);
    const updateSelection = useCanvasStore(selectUpdateSelection);
    const updateViewport = useCanvasStore(selectUpdateViewport);
    const drillIntoNode = useCanvasStore(selectDrillIntoNode);
    const currentPathRef = useRef(currentPath);
    useEffect(()=>{
        currentPathRef.current = currentPath;
    }, [
        currentPath
    ]);
    const addNodeOnConnectEnd = useAddNodeOnConnectEnd();
    const handleAddNodePreviewAction = useCallback((event)=>{
        if (!reactFlowInstance) return;
        createAddNodePreview(event.nodeId, event.handleId, reactFlowInstance, event.position, 'input' === event.handleType ? 'target' : 'source', [], {
            getManifestForNode
        });
    }, [
        reactFlowInstance,
        getManifestForNode
    ]);
    useCanvasEvent('handle:action', handleAddNodePreviewAction);
    const hasInitialized = useRef(false);
    useEffect(()=>{
        if (hasInitialized.current) return;
        hasInitialized.current = true;
        if (initialCanvases && Object.keys(initialCanvases).length > 0) initializeWithData(initialCanvases, initialPath);
        else initializeCanvas();
    }, []);
    const prevCanvasStackRef = useRef(canvasStack);
    useEffect(()=>{
        if (onCanvasesChange && canvasStack !== prevCanvasStackRef.current) {
            prevCanvasStackRef.current = canvasStack;
            onCanvasesChange(canvasStack);
        }
    }, [
        canvasStack,
        onCanvasesChange
    ]);
    const prevPathRef = useRef(currentPath);
    useEffect(()=>{
        if (onPathChange && currentPath !== prevPathRef.current) {
            prevPathRef.current = currentPath;
            onPathChange(currentPath);
        }
    }, [
        currentPath,
        onPathChange
    ]);
    const currentCanvasViewportRef = useRef(currentCanvas?.viewport);
    useEffect(()=>{
        currentCanvasViewportRef.current = currentCanvas?.viewport;
    }, [
        currentCanvas?.viewport
    ]);
    const currentCanvasRef = useRef(currentCanvas);
    useEffect(()=>{
        currentCanvasRef.current = currentCanvas;
    }, [
        currentCanvas
    ]);
    const handleInit = useCallback((instance)=>{
        setReactFlowInstance(instance);
        viewportManager.setReactFlowInstance(instance);
        animatedViewportManager.setReactFlowInstance(instance);
        const viewport = currentCanvasViewportRef.current;
        if (viewport && (0 !== viewport.x || 0 !== viewport.y || 1 !== viewport.zoom)) instance.setViewport(viewport);
    }, []);
    useEffect(()=>{
        const currentCanvasId = currentPath[currentPath.length - 1];
        if (currentCanvasId && lastCanvasIdRef.current && currentCanvasId !== lastCanvasIdRef.current) {
            if (reactFlowInstance && currentCanvas?.viewport) {
                const viewport = currentCanvas.viewport;
                if (isDefaultViewport(viewport)) {
                    currentViewportRef.current = viewport;
                    viewportManager.setCurrentViewport(viewport);
                } else setTimeout(()=>{
                    reactFlowInstance.setViewport(viewport);
                    currentViewportRef.current = viewport;
                    viewportManager.setCurrentViewport(viewport);
                }, 0);
            }
        }
        lastCanvasIdRef.current = currentCanvasId || null;
    }, [
        currentPath,
        reactFlowInstance,
        currentCanvas
    ]);
    useEffect(()=>()=>{
            viewportManager.setReactFlowInstance(null);
        }, []);
    const handleNodesChange = useCallback((changes)=>{
        const canvas = currentCanvasRef.current;
        if (!canvas) return;
        if (!changes.some(shouldPersistNodeChange)) return;
        const updatedNodes = applyNodeChanges(changes, canvas.nodes ?? []);
        updateNodes(updatedNodes);
    }, [
        updateNodes
    ]);
    const handleEdgesChange = useCallback((changes)=>{
        const canvas = currentCanvasRef.current;
        if (!canvas) return;
        const updatedEdges = applyEdgeChanges(changes, canvas.edges ?? []);
        updateEdges(updatedEdges);
    }, [
        updateEdges
    ]);
    const handleSelectionChange = useCallback((params)=>{
        const canvas = currentCanvasRef.current;
        if (!canvas) return;
        const nodeIds = params.nodes.map((n)=>n.id);
        const edgeIds = params.edges.map((e)=>e.id);
        const nodeIdsChanged = nodeIds.length !== canvas.selection.nodeIds.length || !nodeIds.every((id)=>canvas.selection.nodeIds.includes(id));
        const edgeIdsChanged = edgeIds.length !== canvas.selection.edgeIds.length || !edgeIds.every((id)=>canvas.selection.edgeIds.includes(id));
        if (nodeIdsChanged || edgeIdsChanged) updateSelection(nodeIds, edgeIds);
    }, [
        updateSelection
    ]);
    const handleMove = useCallback((_event, viewport)=>{
        currentViewportRef.current = viewport;
        viewportManager.setCurrentViewport(viewport);
    }, []);
    const handleConnect = useCallback((connection)=>{
        const canvas = currentCanvasRef.current;
        if (!connection.source || !connection.target || !canvas) return;
        if (isPreviewEdge(connection)) return;
        const newEdge = {
            id: `${connection.source}-${connection.target}-${Date.now()}`,
            source: connection.source,
            target: connection.target,
            sourceHandle: connection.sourceHandle || void 0,
            targetHandle: connection.targetHandle || void 0
        };
        const hasPreview = canvas.nodes.some((n)=>n.id === PREVIEW_NODE_ID);
        const baseEdges = hasPreview ? canvas.edges.filter((edge)=>!isPreviewEdge(edge)) : canvas.edges;
        updateEdges([
            ...baseEdges,
            newEdge
        ]);
        if (hasPreview) updateNodes(canvas.nodes.filter((n)=>n.id !== PREVIEW_NODE_ID));
    }, [
        updateNodes,
        updateEdges
    ]);
    const handleNavigateToDepth = useCallback(async (depth)=>{
        if (transitionState.isTransitioning) return;
        const path = currentPathRef.current;
        const currentCanvasId = path[path.length - 1];
        if (currentCanvasId && reactFlowInstance) {
            const viewport = reactFlowInstance.getViewport();
            updateViewport(viewport, currentCanvasId);
        }
        await navigateToDepth(depth, shouldAnimate);
    }, [
        navigateToDepth,
        updateViewport,
        reactFlowInstance,
        transitionState.isTransitioning,
        shouldAnimate
    ]);
    const handleToolbarAction = useCallback(async (event)=>{
        if ('drill-in' === event.actionId) await drillIntoNode(event.nodeId, true);
    }, [
        drillIntoNode
    ]);
    const handlePreviousCanvasNodeClick = useCallback((_event, node)=>{
        if (node.data?.childCanvasId) navigateToSiblingCanvas(node.data.childCanvasId, shouldAnimate);
    }, [
        navigateToSiblingCanvas,
        shouldAnimate
    ]);
    const shouldFitView = useMemo(()=>{
        const viewport = currentCanvas?.viewport;
        return viewport ? isDefaultViewport(viewport) : false;
    }, [
        currentCanvas?.viewport
    ]);
    if (!currentCanvas) return /*#__PURE__*/ jsx("div", {
        style: {
            height: '100vh',
            width: '100vw',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center'
        },
        children: /*#__PURE__*/ jsx(Spinner, {})
    });
    return /*#__PURE__*/ jsxs("div", {
        ref: containerRef,
        style: {
            width: '100%',
            height: '100%',
            position: 'relative',
            background: 'var(--canvas-background-secondary)'
        },
        children: [
            breadcrumbs.length > 1 && /*#__PURE__*/ jsx("div", {
                style: {
                    position: 'absolute',
                    top: 16,
                    left: 16,
                    zIndex: 10,
                    background: 'var(--canvas-background)',
                    borderRadius: '8px',
                    padding: '8px 12px',
                    boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)'
                },
                children: /*#__PURE__*/ jsx(Breadcrumb, {
                    items: breadcrumbs.map((crumb, index)=>({
                            label: crumb.name,
                            onClick: index < breadcrumbs.length - 1 ? ()=>handleNavigateToDepth(index) : void 0,
                            startAdornment: 0 === index ? /*#__PURE__*/ jsx(CanvasIcon, {
                                icon: "house",
                                size: 16
                            }) : void 0
                        })),
                    delimiter: /*#__PURE__*/ jsx(CanvasIcon, {
                        icon: "chevron-right",
                        size: 16
                    })
                })
            }),
            previousCanvas && /*#__PURE__*/ jsx("div", {
                style: {
                    position: 'absolute',
                    bottom: 16,
                    left: 16,
                    zIndex: 10
                },
                children: /*#__PURE__*/ jsx(MiniCanvasNavigator, {
                    previousCanvas: previousCanvas,
                    currentCanvasId: currentCanvas?.id,
                    nodeTypes: nodeTypes,
                    onNodeClick: handlePreviousCanvasNodeClick
                })
            }),
            /*#__PURE__*/ jsxs(BaseCanvas, {
                ref: canvasRef,
                nodes: currentCanvas.nodes,
                edges: currentCanvas.edges,
                nodeTypes: nodeTypes,
                onNodesChange: handleNodesChange,
                onEdgesChange: handleEdgesChange,
                onSelectionChange: handleSelectionChange,
                onMove: handleMove,
                onConnect: handleConnect,
                onConnectEnd: addNodeOnConnectEnd,
                onInit: handleInit,
                onToolbarAction: handleToolbarAction,
                mode: mode,
                defaultViewport: currentCanvas.viewport,
                fitView: shouldFitView,
                fitViewOptions: {
                    padding: 0.2,
                    minZoom: 1,
                    maxZoom: 1
                },
                children: [
                    /*#__PURE__*/ jsx(Panel, {
                        position: "bottom-right",
                        children: /*#__PURE__*/ jsx(CanvasPositionControls, {
                            translations: DefaultCanvasTranslations
                        })
                    }),
                    /*#__PURE__*/ jsx(AddNodeManager, {})
                ]
            }, currentCanvas.id)
        ]
    });
};
export { HierarchicalCanvas };
