import { useCallback } from 'react'
import {
  ReactFlow,
  Background,
  Controls,
  MiniMap,
  BackgroundVariant,
  type OnConnect,
  type OnReconnect,
  type OnNodesChange,
  type OnEdgesChange,
  type Node,
  type Edge,
} from '@xyflow/react'
import { nodeTypes } from './nodes/nodeTypes'
import { edgeTypes } from './edges/edgeTypes'
import { getDeviceRenderConfig } from './nodes/deviceRegistry'
import type { DeviceNodeData } from './nodes/DeviceNode'
import type { InteractionMode } from './DiagramHeader'
import { cn } from '@/lib/utils'

interface NetworkCanvasProps {
  nodes: Node[]
  edges: Edge[]
  onNodesChange: OnNodesChange
  onEdgesChange: OnEdgesChange
  onConnect: OnConnect
  onReconnect: OnReconnect<Edge>
  onNodeSelect: (nodeId: string | null) => void
  onEdgeSelect: (edgeId: string | null) => void
  onDrop: (event: React.DragEvent) => void
  onDragOver: (event: React.DragEvent) => void
  onDragLeave?: (event: React.DragEvent) => void
  isDragOver?: boolean
  onNodeContextMenu?: (event: React.MouseEvent, node: Node) => void
  onPaneContextMenu?: (event: MouseEvent | React.MouseEvent) => void
  onPaneClick?: () => void
  interactionMode?: InteractionMode
}

export function NetworkCanvas({
  nodes,
  edges,
  onNodesChange,
  onEdgesChange,
  onConnect,
  onReconnect,
  onNodeSelect,
  onEdgeSelect,
  onDrop,
  onDragOver,
  onDragLeave,
  isDragOver,
  onNodeContextMenu,
  onPaneContextMenu,
  onPaneClick: onPaneClickProp,
  interactionMode = 'select',
}: NetworkCanvasProps) {
  const handleSelectionChange = useCallback(({ nodes: selectedNodes, edges: selectedEdges }: { nodes: Node[]; edges: Edge[] }) => {
    if (selectedNodes.length === 1) {
      onNodeSelect(selectedNodes[0].id)
      onEdgeSelect(null)
    } else if (selectedEdges.length === 1) {
      onEdgeSelect(selectedEdges[0].id)
      onNodeSelect(null)
    } else {
      onNodeSelect(null)
      onEdgeSelect(null)
    }
  }, [onNodeSelect, onEdgeSelect])

  const handlePaneClick = useCallback(() => {
    onNodeSelect(null)
    onEdgeSelect(null)
    onPaneClickProp?.()
  }, [onNodeSelect, onEdgeSelect, onPaneClickProp])

  const getNodeColor = useCallback((node: Node) => {
    if (node.type === 'group') return 'var(--color-bg-elevated)'
    const data = node.data as unknown as DeviceNodeData
    return getDeviceRenderConfig(data?.deviceType || '', data?.category).color
  }, [])

  return (
    <div
      className="relative h-full w-full"
      onDragLeave={onDragLeave}
      onMouseDownCapture={(event) => {
        if (event.button === 1) {
          event.preventDefault()
        }
      }}
    >
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onConnect={onConnect}
        onReconnect={onReconnect}
        onSelectionChange={handleSelectionChange}
        onPaneClick={handlePaneClick}
        onDrop={onDrop}
        onDragOver={onDragOver}
        onNodeContextMenu={onNodeContextMenu}
        onPaneContextMenu={onPaneContextMenu}
        nodeTypes={nodeTypes}
        edgeTypes={edgeTypes}
        defaultEdgeOptions={{ type: 'connection' }}
        edgesReconnectable
        connectOnClick={interactionMode === 'connect'}
        reconnectRadius={20}
        connectionRadius={24}
        deleteKeyCode={['Backspace', 'Delete']}
        multiSelectionKeyCode="Shift"
        panOnDrag={interactionMode === 'pan' ? [0, 1] : [1]}
        selectionOnDrag={interactionMode === 'select'}
        panActivationKeyCode="Space"
        snapToGrid={true}
        snapGrid={[20, 20]}
        fitView
        className={cn(
          'bg-page',
          interactionMode === 'pan' && 'cursor-grab active:cursor-grabbing',
          interactionMode === 'connect' && 'rf-connect-mode cursor-crosshair',
        )}
      >
        <Background variant={BackgroundVariant.Dots} color="var(--color-border-default)" gap={20} size={1} />
        <Controls className="!border-default !bg-card [&>button]:!border-default [&>button]:!bg-card [&>button]:!fill-text-primary" />
        <MiniMap
          nodeColor={getNodeColor}
          maskColor="rgba(0,0,0,0.5)"
          className="!border-default !bg-card"
          position="bottom-right"
        />
      </ReactFlow>
      {isDragOver && (
        <div className="pointer-events-none absolute inset-2 z-10 flex items-center justify-center rounded-lg border-2 border-dashed border-accent/30">
          <span className="rounded-md bg-card/80 px-3 py-1.5 text-sm text-muted-foreground">
            Drop to add
          </span>
        </div>
      )}
    </div>
  )
}
