import {
   Background,
   BackgroundVariant,
   MiniMap,
   type MiniMapProps,
   ReactFlow,
   type ReactFlowProps,
   addEdge,
   useEdgesState,
   useNodesState,
   useReactFlow,
} from "@xyflow/react";
import { type ReactNode, useCallback, useEffect, useState } from "react";
import { useTheme } from "ui/client/use-theme";

type CanvasProps = ReactFlowProps & {
   externalProvider?: boolean;
   backgroundStyle?: "lines" | "dots";
   minimap?: boolean | MiniMapProps;
   children?: Element | ReactNode;
   onDropNewNode?: (base: any) => any;
   onDropNewEdge?: (base: any) => any;
};

export function Canvas({
   nodes: _nodes,
   edges: _edges,
   externalProvider,
   backgroundStyle = "lines",
   minimap = false,
   children,
   onDropNewNode,
   onDropNewEdge,
   ...props
}: CanvasProps) {
   const [nodes, setNodes, onNodesChange] = useNodesState(_nodes ?? []);
   const [edges, setEdges, onEdgesChange] = useEdgesState(_edges ?? []);
   const { screenToFlowPosition } = useReactFlow();
   const { theme } = useTheme();

   const [isCommandPressed, setIsCommandPressed] = useState(false);
   const [isSpacePressed, setIsSpacePressed] = useState(false);
   const [isPointerPressed, setIsPointerPressed] = useState(false);

   const handleKeyDown = (event: KeyboardEvent) => {
      if (event.metaKey) {
         setIsCommandPressed(true);
      }
      if (event.key === " ") {
         //event.preventDefault(); // Prevent default space scrolling behavior
         setIsSpacePressed(true);
      }
   };

   const handleKeyUp = (event: KeyboardEvent) => {
      if (!event.metaKey) {
         setIsCommandPressed(false);
      }
      if (event.key === " ") {
         setIsSpacePressed(false);
      }
   };

   const handlePointerDown = () => {
      if (isSpacePressed) {
         setIsPointerPressed(false);
         return;
      }
      setIsPointerPressed(true);
   };

   const handlePointerUp = () => {
      if (isSpacePressed) {
         setIsPointerPressed(false);
         return;
      }
      setIsPointerPressed(false);
   };

   useEffect(() => {
      document.querySelector("html")?.classList.add("fixed");

      // Add global key listeners
      window.addEventListener("keydown", handleKeyDown);
      window.addEventListener("keyup", handleKeyUp);

      // Add global pointer listeners
      window.addEventListener("pointerdown", handlePointerDown);
      window.addEventListener("pointerup", handlePointerUp);

      // Cleanup event listeners on component unmount
      return () => {
         window.removeEventListener("keydown", handleKeyDown);
         window.removeEventListener("keyup", handleKeyUp);
         window.removeEventListener("pointerdown", handlePointerDown);
         window.removeEventListener("pointerup", handlePointerUp);
         document.querySelector("html")?.classList.remove("fixed");
      };
   }, []);

   //console.log("mode", { cmd: isCommandPressed, space: isSpacePressed, mouse: isPointerPressed });

   useEffect(() => {
      setNodes(_nodes ?? []);
      setEdges(_edges ?? []);
   }, [_nodes, _edges]);

   const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), []);

   const onConnectEnd = useCallback(
      (event, connectionState) => {
         if (!onDropNewNode || !onDropNewEdge) return;

         const { fromNode, fromHandle, fromPosition } = connectionState;
         // when a connection is dropped on the pane it's not valid
         if (!connectionState.isValid) {
            console.log("conn", { event, connectionState });
            // we need to remove the wrapper bounds, in order to get the correct position

            const { clientX, clientY } =
               "changedTouches" in event ? event.changedTouches[0] : event;
            const newNode = onDropNewNode({
               id: "select",
               type: "default",
               data: { label: "" },
               position: screenToFlowPosition({
                  x: clientX,
                  y: clientY,
               }),
               origin: [0.0, 0.0],
            });

            setNodes((nds) => nds.concat(newNode as any));
            setEdges((eds) =>
               eds.concat(
                  onDropNewNode({
                     id: newNode.id,
                     source: connectionState.fromNode.id,
                     target: newNode.id,
                  }),
               ),
            );
         }
      },
      [screenToFlowPosition],
   );
   //console.log("edges1", edges);

   return (
      <ReactFlow
         colorMode={theme}
         onConnect={onConnect}
         onConnectEnd={onConnectEnd}
         className={
            isCommandPressed
               ? "cursor-zoom-in"
               : isSpacePressed
                 ? isPointerPressed
                    ? "cursor-grabbing"
                    : "cursor-grab"
                 : ""
         }
         proOptions={{
            hideAttribution: true,
         }}
         fitView
         fitViewOptions={{
            maxZoom: 1.5,
            ...props.fitViewOptions,
         }}
         nodeDragThreshold={25}
         panOnScrollSpeed={1}
         snapToGrid
         nodes={nodes}
         edges={edges}
         onNodesChange={onNodesChange}
         onEdgesChange={onEdgesChange}
         nodesConnectable={false}
         /*panOnDrag={isSpacePressed}*/
         panOnDrag={true}
         zoomOnScroll={isCommandPressed}
         panOnScroll={!isCommandPressed}
         zoomOnDoubleClick={false}
         selectionOnDrag={!isSpacePressed}
         {...props}
      >
         {backgroundStyle === "lines" && (
            <Background
               color={theme === "light" ? "rgba(0,0,0,.05)" : "rgba(255,255,255,.1)"}
               gap={[50, 50]}
               variant={BackgroundVariant.Lines}
            />
         )}
         {backgroundStyle === "dots" && (
            <Background color={theme === "light" ? "rgba(0,0,0,.5)" : "rgba(255,255,255,.2)"} />
         )}
         {minimap && <MiniMap {...(typeof minimap === "object" ? minimap : {})} />}
         {children}
      </ReactFlow>
   );
}
