import { Position, useReactFlow, useStore } from "../../../../xyflow/react.js";
import { useCallback, useEffect } from "react";
import { getNodeDimensions } from "../../../../utils/container.js";
import { waypointsPositionallyEqual } from "../waypoints.js";
function defaultIsRoutable(edge) {
    const data = edge.data;
    return data?.routing === 'waypoint' && !data.waypoints?.length;
}
function useGraphRouter(router, isRoutable = defaultIsRoutable) {
    const { getNodes, getEdges, setEdges } = useReactFlow();
    const signature = useStore(useCallback((state)=>routeSignature(state, isRoutable), [
        isRoutable
    ]));
    useEffect(()=>{
        if (!signature) return;
        let cancelled = false;
        const apply = (routes)=>{
            if (cancelled) return;
            const byId = new Map(routes.map((r)=>[
                    r.edgeId,
                    r
                ]));
            setEdges((current)=>{
                let next = null;
                for(let i = 0; i < current.length; i++){
                    const edge = current[i];
                    const reconciled = reconcileEdge(edge, byId, isRoutable);
                    if (reconciled !== edge) {
                        next ??= [
                            ...current
                        ];
                        next[i] = reconciled;
                    }
                }
                return next ?? current;
            });
        };
        const edges = getEdges();
        if (!edges.some(isRoutable)) {
            const hasStale = edges.some((edge)=>edge.data?.routedWaypoints !== void 0);
            if (hasStale) apply([]);
            return;
        }
        const request = buildRequest(getNodes(), edges, isRoutable);
        if (0 === request.edges.length) return void apply([]);
        const result = router.route(request);
        if (result instanceof Promise) result.then(apply).catch((err)=>{
            if (cancelled) return;
            console.error('[useGraphRouter] router.route() rejected:', err);
        });
        else apply(result);
        return ()=>{
            cancelled = true;
        };
    }, [
        signature,
        router,
        isRoutable,
        getNodes,
        getEdges,
        setEdges
    ]);
}
function reconcileEdge(edge, routesById, isRoutable) {
    const data = edge.data;
    if (!isRoutable(edge)) {
        if (data?.routedWaypoints === void 0) return edge;
        const { routedWaypoints: _removed, ...rest } = data;
        return {
            ...edge,
            data: rest
        };
    }
    const route = routesById.get(edge.id);
    if (!route) return edge;
    const prev = data?.routedWaypoints ?? [];
    if (waypointsPositionallyEqual(prev, route.waypoints)) return edge;
    return {
        ...edge,
        data: {
            ...edge.data,
            routedWaypoints: route.waypoints
        }
    };
}
function routeSignature(state, isRoutable) {
    let sig = '';
    for (const node of state.nodes){
        const { width, height } = getNodeDimensions(node);
        sig += `${node.id}:${node.position.x},${node.position.y},${width},${height}|`;
    }
    for (const edge of state.edges)sig += `${edge.id}:${edge.source}/${edge.sourceHandle ?? ''}>${edge.target}/${edge.targetHandle ?? ''}:${isRoutable(edge) ? 'r' : 'm'}|`;
    return sig;
}
function buildRequest(nodes, edges, isRoutable) {
    const routeNodes = nodes.map((n)=>{
        const { width, height } = getNodeDimensions(n);
        return {
            id: n.id,
            x: n.position.x,
            y: n.position.y,
            width,
            height
        };
    });
    const nodeMap = new Map(routeNodes.map((n)=>[
            n.id,
            n
        ]));
    const routeEdges = [];
    for (const edge of edges){
        if (!isRoutable(edge)) continue;
        const source = nodeMap.get(edge.source);
        const target = nodeMap.get(edge.target);
        if (source && target) routeEdges.push({
            edgeId: edge.id,
            source: {
                nodeId: edge.source,
                handleId: edge.sourceHandle ?? null,
                x: source.x + source.width,
                y: source.y + source.height / 2,
                position: Position.Right
            },
            target: {
                nodeId: edge.target,
                handleId: edge.targetHandle ?? null,
                x: target.x,
                y: target.y + target.height / 2,
                position: Position.Left
            }
        });
    }
    return {
        nodes: routeNodes,
        edges: routeEdges
    };
}
export { defaultIsRoutable, useGraphRouter };
