import { jsx, jsxs } from "react/jsx-runtime";
import { Position, useReactFlow, useStore, useUpdateNodeInternals } from "../../xyflow/react.js";
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { DEFAULT_NODE_SIZE, DEFAULT_RECTANGLE_NODE_WIDTH, GRID_SPACING, NODE_BADGE_INSET_CIRCLE, NODE_BADGE_INSET_SQUARE, NODE_BADGE_SIZE, NODE_BORDER_SIZE, NODE_CONTAINER_RADIUS_RATIO, NODE_HEIGHT_DEFAULT, NODE_HEIGHT_FOOTER_BUTTON, NODE_HEIGHT_FOOTER_DOUBLE, NODE_HEIGHT_FOOTER_SINGLE, NODE_INNER_ICON_RATIO, NODE_INNER_RADIUS_RATIO, NODE_INNER_SHAPE_RATIO } from "../../constants.js";
import { useNodeTypeRegistry } from "../../core/index.js";
import { useElementValidationStatus, useNodeExecutionState } from "../../hooks/index.js";
import { resolveAdornments } from "../../utils/adornment-resolver.js";
import { CanvasIcon, getIcon } from "../../utils/icon-registry.js";
import { resolveDisplay, resolveHandles } from "../../utils/manifest-resolver.js";
import { selectIsConnecting } from "../../utils/NodeUtils.js";
import { areNodePropsEqualIgnoringPosition } from "../../utils/nodePropsEqual.js";
import { resolveToolbar } from "../../utils/toolbar-resolver.js";
import { useBaseCanvasMode } from "../BaseCanvas/BaseCanvasModeProvider.js";
import { useCanvasTheme } from "../BaseCanvas/CanvasThemeContext.js";
import { useConnectedHandles } from "../BaseCanvas/ConnectedHandlesContext.js";
import { useSelectionState } from "../BaseCanvas/SelectionStateContext.js";
import { SmartHandle, SmartHandleProvider } from "../ButtonHandle/SmartHandle.js";
import { useButtonHandles } from "../ButtonHandle/useButtonHandles.js";
import { InitialsBadge } from "../shared/InitialsBadge.js";
import { NodeToolbar } from "../Toolbar/index.js";
import { BaseBadgeSlot } from "./BaseNodeBadgeSlot.js";
import { useBaseNodeOverrideConfig } from "./BaseNodeConfigContext.js";
import { BaseContainer } from "./BaseNodeContainer.js";
import { BaseInnerShape } from "./BaseNodeInnerShape.js";
import { MissingManifestNode } from "./BaseNodeMissingManifest.js";
import { NodeLabel } from "./NodeLabel.js";
const getContainerWidth = (shape, width)=>{
    const defaultWidth = 'rectangle' === shape ? DEFAULT_RECTANGLE_NODE_WIDTH : DEFAULT_NODE_SIZE;
    if (width && width !== DEFAULT_NODE_SIZE && width !== DEFAULT_RECTANGLE_NODE_WIDTH) return width;
    return defaultWidth;
};
const getContainerHeight = (height, hasFooter, footerVariant)=>{
    if (hasFooter) switch(footerVariant){
        case 'button':
            return NODE_HEIGHT_FOOTER_BUTTON;
        case 'single':
            return NODE_HEIGHT_FOOTER_SINGLE;
        case 'double':
            return NODE_HEIGHT_FOOTER_DOUBLE;
        default:
            return 'auto';
    }
    return height || NODE_HEIGHT_DEFAULT;
};
const BaseNodeComponent = (props)=>{
    const { type, data, selected, id, dragging, width, height, parentId } = props;
    const { onHandleAction: onHandleActionProp, onHandleMouseEnter: onHandleMouseEnterProp, onHandleMouseLeave: onHandleMouseLeaveProp, onActionNeeded, shouldShowAddButtonFn: shouldShowAddButtonFnProp, shouldShowButtonHandleNotchesFn: shouldShowButtonHandleNotchesFnProp, toolbarConfig: toolbarConfigProp, handleConfigurations: handleConfigurationsProp, adornments: adornmentsProp, suggestionType, disabled, executionStatusOverride, labelTooltip, labelBackgroundColor, footerVariant, footerComponent, subLabelComponent, iconComponent } = useBaseNodeOverrideConfig();
    const updateNodeInternals = useUpdateNodeInternals();
    const { updateNodeData, updateNode } = useReactFlow();
    const containerRef = useRef(null);
    const [isHovered, setIsHovered] = useState(false);
    const [isFocused, setIsFocused] = useState(false);
    const syncedHeightRef = useRef(void 0);
    const baseHeightRef = useRef(DEFAULT_NODE_SIZE);
    if (height && height !== syncedHeightRef.current) baseHeightRef.current = height;
    const executionState = useNodeExecutionState(id);
    const validationState = useElementValidationStatus(id);
    const nodeTypeRegistry = useNodeTypeRegistry();
    const { mode } = useBaseCanvasMode();
    const connectedHandleIds = useConnectedHandles(id);
    const isConnecting = useStore(selectIsConnecting);
    const { multipleNodesSelected } = useSelectionState();
    const { isDarkMode } = useCanvasTheme();
    const manifest = useMemo(()=>nodeTypeRegistry.getManifest(type), [
        type,
        nodeTypeRegistry
    ]);
    const statusContext = useMemo(()=>({
            nodeId: id,
            executionState: executionStatusOverride ?? executionState,
            validationState,
            isConnecting,
            isSelected: selected,
            isDragging: dragging,
            mode
        }), [
        id,
        executionStatusOverride,
        executionState,
        validationState,
        isConnecting,
        selected,
        dragging,
        mode
    ]);
    const onHandleActionCallback = onHandleActionProp;
    const shouldShowAddButtonFn = shouldShowAddButtonFnProp;
    const shouldShowButtonHandleNotchesFn = shouldShowButtonHandleNotchesFnProp;
    const executionStatus = executionStatusOverride ?? ('string' == typeof executionState ? executionState : executionState?.status);
    const display = useMemo(()=>resolveDisplay(manifest?.display, {
            ...data,
            nodeId: id
        }), [
        manifest,
        data,
        id
    ]);
    const isStacked = Boolean(manifest?.drillable || data?.isCollapsed);
    const Icon = useMemo(()=>{
        if (void 0 !== iconComponent) return iconComponent;
        if (display.icon) {
            const IconComponent = getIcon(display.icon);
            return IconComponent ? /*#__PURE__*/ jsx(IconComponent, {}) : null;
        }
        return /*#__PURE__*/ jsx(InitialsBadge, {
            name: display.label,
            size: "var(--icon-size)"
        });
    }, [
        iconComponent,
        display.icon,
        display.label
    ]);
    const handleConfigurations = useMemo(()=>{
        if (handleConfigurationsProp && Array.isArray(handleConfigurationsProp)) return handleConfigurationsProp;
        const dataHandleConfigs = data?.handleConfigurations;
        if (dataHandleConfigs && Array.isArray(dataHandleConfigs)) return dataHandleConfigs;
        if (!manifest) return [];
        const resolved = resolveHandles(manifest.handleConfiguration, {
            ...data,
            nodeId: id
        });
        return resolved.map((group)=>({
                position: group.position,
                handles: group.handles.map((h)=>({
                        id: h.id,
                        type: h.type,
                        handleType: h.handleType,
                        label: h.label,
                        visible: h.visible,
                        showButton: h.showButton,
                        labelVisibility: h.labelVisibility,
                        constraints: h.constraints
                    })),
                visible: group.visible
            }));
    }, [
        handleConfigurationsProp,
        manifest,
        data,
        id
    ]);
    const toolbarConfig = useMemo(()=>{
        if (void 0 !== toolbarConfigProp) return null === toolbarConfigProp ? void 0 : toolbarConfigProp;
        return manifest ? resolveToolbar(manifest, statusContext) : void 0;
    }, [
        toolbarConfigProp,
        manifest,
        statusContext
    ]);
    const adornments = useMemo(()=>{
        const adornmentsFromProps = adornmentsProp ?? {};
        const adornmentsFromResolver = resolveAdornments(statusContext);
        return {
            ...adornmentsFromResolver,
            ...adornmentsFromProps
        };
    }, [
        adornmentsProp,
        statusContext
    ]);
    const computedHeight = useMemo(()=>{
        const leftHandles = handleConfigurations.filter((config)=>config.position === Position.Left && false !== config.visible).reduce((count, config)=>count + config.handles.filter((h)=>false !== h.visible).length, 0);
        const rightHandles = handleConfigurations.filter((config)=>config.position === Position.Right && false !== config.visible).reduce((count, config)=>count + config.handles.filter((h)=>false !== h.visible).length, 0);
        const leftRightHandles = Math.max(leftHandles, rightHandles);
        const minNodeHeight = (2 * leftRightHandles + 2) * GRID_SPACING;
        return Math.max(baseHeightRef.current, minNodeHeight);
    }, [
        handleConfigurations,
        height
    ]);
    useEffect(()=>{
        updateNodeInternals(id);
    }, [
        handleConfigurations,
        id,
        updateNodeInternals
    ]);
    useEffect(()=>{
        if (void 0 !== computedHeight && computedHeight !== height) {
            syncedHeightRef.current = computedHeight;
            const frameId = requestAnimationFrame(()=>{
                updateNode(id, {
                    height: computedHeight
                });
                updateNodeInternals(id);
            });
            return ()=>{
                cancelAnimationFrame(frameId);
            };
        }
    }, [
        computedHeight,
        height,
        id,
        updateNode,
        updateNodeInternals
    ]);
    const displayLabel = display.label;
    const displayShape = display.shape ?? 'square';
    const displayBackground = display.background;
    const displayColor = display.color;
    const displayShadow = display.shadow ?? true;
    const displayIconBackground = isDarkMode ? display.iconBackgroundDark ?? display.iconBackground : display.iconBackground;
    const displayLabelTooltip = labelTooltip;
    const displayLabelBackgroundColor = labelBackgroundColor;
    const displayFooterVariant = footerVariant;
    const displaySubLabel = useMemo(()=>{
        if (void 0 !== subLabelComponent) return subLabelComponent;
        return display.subLabel;
    }, [
        subLabelComponent,
        display.subLabel
    ]);
    const displayFooter = footerComponent;
    const hasFooter = !!displayFooter;
    const containerWidth = getContainerWidth(displayShape, width);
    const containerHeight = getContainerHeight(height, hasFooter, displayFooterVariant);
    const nodeVars = useMemo(()=>{
        const numH = 'number' == typeof containerHeight ? containerHeight : DEFAULT_NODE_SIZE;
        const getRadius = (basis, ratio)=>'circle' === displayShape ? '50%' : hasFooter ? `${GRID_SPACING}px` : `${basis * ratio}px`;
        const radiusBasis = Math.min(containerWidth, numH);
        const nodeRadius = getRadius(radiusBasis, NODE_CONTAINER_RADIUS_RATIO);
        const effectiveH = hasFooter ? DEFAULT_NODE_SIZE : numH;
        const effectiveW = hasFooter ? DEFAULT_NODE_SIZE : containerWidth;
        const innerBasis = Math.min(effectiveH, effectiveW);
        const innerRadius = getRadius(innerBasis, NODE_INNER_RADIUS_RATIO);
        const gap = innerBasis * (1 - NODE_INNER_SHAPE_RATIO);
        const keepSquare = 'circle' === displayShape || 'rectangle' === displayShape;
        const innerW = keepSquare ? innerBasis - gap : effectiveW - gap;
        const innerH = keepSquare ? innerBasis - gap : effectiveH - gap;
        return {
            '--node-w': `${containerWidth}px`,
            '--node-h': 'number' == typeof containerHeight ? `${containerHeight}px` : 'auto',
            '--node-radius': nodeRadius,
            '--node-gap': `${(gap - 2 * NODE_BORDER_SIZE) / 2}px`,
            '--inner-w': `${innerW}px`,
            '--inner-h': `${innerH}px`,
            '--inner-radius': innerRadius,
            '--icon-size': `${innerBasis * NODE_INNER_ICON_RATIO}px`
        };
    }, [
        containerWidth,
        containerHeight,
        displayShape,
        hasFooter
    ]);
    const interactionState = useMemo(()=>{
        if (disabled) return 'disabled';
        if (dragging) return 'drag';
        if (selected) return 'selected';
        if (isFocused) return 'focus';
        if (isHovered) return 'hover';
        return 'default';
    }, [
        disabled,
        dragging,
        selected,
        isHovered,
        isFocused
    ]);
    const shouldShowHandles = useMemo(()=>(isConnecting || selected || isHovered) && !dragging, [
        isConnecting,
        selected,
        isHovered,
        dragging
    ]);
    const hasVisibleBottomHandles = useMemo(()=>{
        if (!handleConfigurations || !Array.isArray(handleConfigurations) || !selected || 'circle' === displayShape) return false;
        return handleConfigurations.some((config)=>config.position === Position.Bottom && config.handles.length > 0 && false !== config.visible && (config.handles.some((h)=>'source' === h.type) || config.handles.some((h)=>h.showButton)));
    }, [
        handleConfigurations,
        selected,
        displayShape
    ]);
    const handleMouseEnter = useCallback(()=>setIsHovered(true), []);
    const handleMouseLeave = useCallback(()=>setIsHovered(false), []);
    const handleFocus = useCallback(()=>setIsFocused(true), []);
    const handleBlur = useCallback(()=>setIsFocused(false), []);
    const handleLabelChange = useCallback((values)=>{
        const newDisplay = {
            ...data.display
        };
        for (const labelKey of Object.keys(values))if (values[labelKey]) newDisplay[labelKey] = values[labelKey];
        else delete newDisplay[labelKey];
        updateNodeData(id, {
            display: newDisplay
        });
    }, [
        id,
        data.display,
        updateNodeData
    ]);
    const showNotches = shouldShowButtonHandleNotchesFn ? shouldShowButtonHandleNotchesFn({
        isConnecting,
        isSelected: selected,
        isHovered
    }) : isConnecting || isHovered || selected;
    const handleAction = useCallback((event)=>{
        setIsHovered(false);
        setIsFocused(false);
        if (onHandleActionCallback) onHandleActionCallback(event);
    }, [
        onHandleActionCallback
    ]);
    const useSmartHandles = data?.useSmartHandles ?? false;
    const toolbarPosition = toolbarConfig?.position ?? (toolbarConfig ? Position.Top : void 0);
    const toolbarSideHandleAffordances = useMemo(()=>{
        let hasButton = false;
        let hasLabel = false;
        if (!toolbarPosition || !handleConfigurations?.length) return {
            hasButton,
            hasLabel
        };
        for (const config of handleConfigurations)if (config.position === toolbarPosition && false !== config.visible) for (const handle of config.handles){
            if (false === handle.visible) continue;
            const showButton = useSmartHandles ? handle.showButton : false !== handle.showButton;
            if ('source' === handle.type && showButton) hasButton = true;
            if (handle.label) hasLabel = true;
        }
        return {
            hasButton,
            hasLabel
        };
    }, [
        toolbarPosition,
        handleConfigurations
    ]);
    const offsetToolbar = useMemo(()=>{
        if (multipleNodesSelected) return false;
        const { hasButton, hasLabel } = toolbarSideHandleAffordances;
        const isAddButtonShown = ()=>{
            if (useSmartHandles) return 'design' === mode && !!selected;
            const showAddButton = 'design' === mode && !isConnecting && !dragging;
            if (shouldShowAddButtonFn) return shouldShowAddButtonFn({
                showAddButton,
                selected: !!selected
            });
            return showAddButton && (!!selected || isHovered);
        };
        if (hasButton && isAddButtonShown()) return 'button';
        if (hasLabel) return 'label';
        return false;
    }, [
        toolbarSideHandleAffordances,
        mode,
        multipleNodesSelected,
        useSmartHandles,
        selected,
        isHovered,
        isConnecting,
        dragging,
        shouldShowAddButtonFn
    ]);
    const buttonHandleElements = useButtonHandles({
        handleConfigurations,
        shouldShowHandles,
        handleAction,
        handleMouseEnter: onHandleMouseEnterProp,
        handleMouseLeave: onHandleMouseLeaveProp,
        nodeId: id,
        selected: selected ?? false,
        hovered: isHovered,
        showAddButton: 'design' === mode && !multipleNodesSelected && !isConnecting && !dragging,
        showNotches,
        nodeWidth: width,
        nodeHeight: height,
        shouldShowAddButtonFn,
        portalActions: !!parentId
    });
    const smartHandleElements = useMemo(()=>{
        if (!useSmartHandles) return null;
        if (!handleConfigurations || !Array.isArray(handleConfigurations)) return null;
        let handleIndex = 0;
        const handles = handleConfigurations.flatMap((config)=>config.handles.map((handle)=>{
                const defaultPosition = config.position;
                const configVisible = config.visible ?? true;
                const currentIndex = handleIndex++;
                const handleVisualType = handle.handleType ?? ('source' === handle.type ? 'output' : 'input');
                const hasConnection = connectedHandleIds.has(handle.id);
                const isVisible = hasConnection || shouldShowHandles && configVisible;
                const shouldShowButton = 'design' === mode && selected && handle.showButton && !multipleNodesSelected;
                return /*#__PURE__*/ jsx(SmartHandle, {
                    type: handle.type,
                    id: handle.id,
                    defaultPosition: defaultPosition,
                    handleType: handleVisualType,
                    nodeWidth: width,
                    nodeHeight: height,
                    label: handle.label,
                    showButton: shouldShowButton,
                    selected: selected,
                    showNotches: showNotches,
                    onAction: handleAction,
                    visible: isVisible,
                    configOrder: currentIndex
                }, `${handle.id}-${handle.type}`);
            }));
        return handles.length > 0 ? handles : null;
    }, [
        useSmartHandles,
        handleConfigurations,
        shouldShowHandles,
        width,
        height,
        mode,
        selected,
        showNotches,
        handleAction,
        multipleNodesSelected,
        connectedHandleIds
    ]);
    const handleElements = useSmartHandles ? smartHandleElements : buttonHandleElements;
    if (!manifest) return /*#__PURE__*/ jsx("div", {
        ref: containerRef,
        className: "relative",
        style: nodeVars,
        onMouseEnter: handleMouseEnter,
        onMouseLeave: handleMouseLeave,
        onFocus: handleFocus,
        onBlur: handleBlur,
        children: /*#__PURE__*/ jsx(MissingManifestNode, {
            type: data.nodeType,
            isSelected: selected,
            isHovered: isHovered,
            interactionState: interactionState
        })
    });
    const nodeContent = /*#__PURE__*/ jsxs("div", {
        ref: containerRef,
        className: "relative",
        style: nodeVars,
        onMouseEnter: handleMouseEnter,
        onMouseLeave: handleMouseLeave,
        onFocus: handleFocus,
        onBlur: handleBlur,
        children: [
            /*#__PURE__*/ jsxs(BaseContainer, {
                shape: displayShape,
                isSelected: selected,
                isHovered: isHovered,
                isStacked: isStacked,
                interactionState: interactionState,
                executionStatus: executionStatus,
                validationStatus: validationState?.validationStatus,
                suggestionType: suggestionType,
                hasFooter: hasFooter,
                background: displayBackground,
                loading: data.loading,
                shadow: displayShadow,
                children: [
                    (Icon || data.loading) && /*#__PURE__*/ jsx(BaseInnerShape, {
                        color: displayColor,
                        background: displayIconBackground,
                        loading: data.loading,
                        children: data.loading ? null : Icon
                    }),
                    adornments?.topLeft && /*#__PURE__*/ jsx(BaseBadgeSlot, {
                        position: "top-left",
                        shape: displayShape,
                        children: adornments.topLeft
                    }),
                    adornments?.topRight && 'ActionNeeded' !== executionStatus && /*#__PURE__*/ jsx(BaseBadgeSlot, {
                        position: "top-right",
                        shape: displayShape,
                        children: adornments.topRight
                    }),
                    adornments?.bottomRight && /*#__PURE__*/ jsx(BaseBadgeSlot, {
                        position: "bottom-right",
                        shape: displayShape,
                        children: adornments.bottomRight
                    }),
                    adornments?.bottomLeft && /*#__PURE__*/ jsx(BaseBadgeSlot, {
                        position: "bottom-left",
                        shape: displayShape,
                        children: adornments.bottomLeft
                    }),
                    /*#__PURE__*/ jsx(NodeLabel, {
                        label: displayLabel,
                        subLabel: displaySubLabel,
                        labelTooltip: displayLabelTooltip,
                        labelBackgroundColor: displayLabelBackgroundColor,
                        shape: displayShape,
                        hasBottomHandles: hasVisibleBottomHandles,
                        selected: selected,
                        dragging: dragging,
                        readonly: 'design' !== mode,
                        onChange: handleLabelChange
                    }),
                    displayFooter && /*#__PURE__*/ jsx("div", {
                        className: "basis-full pt-0.5 min-w-0 overflow-hidden",
                        children: displayFooter
                    })
                ]
            }),
            toolbarConfig && /*#__PURE__*/ jsx(NodeToolbar, {
                nodeId: id,
                config: toolbarConfig,
                expanded: selected || isHovered,
                hidden: dragging || multipleNodesSelected,
                offsetToolbar: offsetToolbar
            }),
            handleElements,
            'ActionNeeded' === executionStatus && (()=>{
                const badgeInset = 'circle' === displayShape ? NODE_BADGE_INSET_CIRCLE : NODE_BADGE_INSET_SQUARE;
                return /*#__PURE__*/ jsxs("button", {
                    type: "button",
                    className: "nodrag absolute z-10 flex cursor-pointer items-center gap-1 rounded-full bg-amber-400 text-[11px] font-semibold text-stone-900 shadow-sm transition-colors hover:bg-amber-300 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-500",
                    style: {
                        top: badgeInset,
                        left: `calc(var(--node-w) - ${badgeInset + NODE_BADGE_SIZE}px)`,
                        height: NODE_BADGE_SIZE,
                        minWidth: NODE_BADGE_SIZE,
                        paddingInline: '4px 10px'
                    },
                    onClick: (e)=>{
                        e.stopPropagation();
                        onActionNeeded?.(id);
                    },
                    children: [
                        /*#__PURE__*/ jsx(CanvasIcon, {
                            icon: "flag",
                            size: 12
                        }),
                        /*#__PURE__*/ jsx("span", {
                            className: "whitespace-nowrap",
                            children: "Action needed"
                        })
                    ]
                });
            })()
        ]
    });
    if (useSmartHandles) return /*#__PURE__*/ jsx(SmartHandleProvider, {
        nodeWidth: width,
        nodeHeight: height,
        children: nodeContent
    });
    return nodeContent;
};
const BaseNode = /*#__PURE__*/ memo(BaseNodeComponent, areNodePropsEqualIgnoringPosition);
export { BaseNode };
