import React from 'react'
import ReactFlow, {
  Controls,
  Background,
  MiniMap,
  addEdge,
  applyEdgeChanges,
  applyNodeChanges,
  useNodesState,
  useEdgesState,
  Node,
  Edge,
  Connection,
  NodeChange,
  EdgeChange,
} from 'reactflow'
import ConfigNode from './components/graph/ConfigNode'
import FunctionNode from './components/graph/FunctionNode'
import RenderingNode from './components/graph/RenderingNode'
import VariableNode from './components/graph/VariableNode'
import FlowContext from './lib/flowContext'
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuGroup,
  ContextMenuItem,
  ContextMenuLabel,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"
// Generate short unique node ids when missing
const genId = () => `node_${Math.random().toString(36).slice(2, 9)}`

const initialNodes: Node[] = [
  {
    id: 'config-1',
    position: { x: 50, y: 50 },
    data: { yaml: "# example:\nmessage: Hello from config" },
    type: 'config',
  },
  {
    id: 'render-1',
    position: { x: 350, y: 80 },
    data: {},
    type: 'render',
  },
].map((n) => ({ ...n, id: n.id ?? genId() }))

const initialEdges: Edge[] = [{ id: 'e-config-render', source: 'config-1', target: 'render-1' }]

export default function App() {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes)
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)
  const [rfInstance, setRfInstance] = React.useState<any | null>(null)

  const wrapperRef = React.useRef<HTMLDivElement | null>(null)
  const lastClickRef = React.useRef<{ clientX: number; clientY: number } | null>(null)
  const [contextTarget, setContextTarget] = React.useState<null | { type: 'canvas' | 'node'; nodeId?: string; clientX: number; clientY: number }>(null)

  const nodeTypes = React.useMemo(
    () => ({ config: ConfigNode, render: RenderingNode, variable: VariableNode, function: FunctionNode }),
    []
  )

  const onConnect = React.useCallback(
    (params: Connection) => setEdges((eds) => addEdge(params, eds)),
    [setEdges]
  )

  const onInit = React.useCallback((instance: any) => {
    setRfInstance(instance)
  }, [])

  const onCanvasContextMenu = React.useCallback((ev: React.MouseEvent) => {
    ev.preventDefault()
    const target = ev.target as HTMLElement
    const nodeEl = target.closest('.react-flow__node') as HTMLElement | null
    const handleEl = target.closest('.react-flow__handle') as HTMLElement | null
    const clientX = ev.clientX
    const clientY = ev.clientY

    lastClickRef.current = { clientX, clientY }

    if (nodeEl && !handleEl) {
      const nodeId = nodeEl.dataset?.id || nodeEl.getAttribute('data-id') || (nodeEl.id && nodeEl.id.startsWith('reactflow__node-') ? nodeEl.id.replace('reactflow__node-', '') : undefined)
      setContextTarget({ type: 'node', nodeId, clientX, clientY })
      return
    }

    setContextTarget({ type: 'canvas', clientX, clientY })
  }, [])

  const createNode = React.useCallback(
    (type: string) => {
      if (!rfInstance) return
      const click = contextTarget ?? lastClickRef.current
      const clientX = click?.clientX ?? window.innerWidth / 2
      const clientY = click?.clientY ?? window.innerHeight / 2
      const rect = wrapperRef.current?.getBoundingClientRect()
      const point = rect ? { x: clientX - rect.left, y: clientY - rect.top } : { x: clientX, y: clientY }
      let position = point
      try {
        // project to flow coords when possible
        position = rfInstance.project ? rfInstance.project(point) : point
      } catch (e) {
        // ignore and use raw point
      }

      const id = genId()
      const typeMap: Record<string, Node['type']> = {
        config: 'config',
        render: 'render',
        variable: 'variable',
        function: 'function',
      }
      const dataMap: Record<string, any> = {
        config: { yaml: '# Enter YAML here\n', title: `config-${id}` },
        render: {},
        variable: { value: '', valueType: 'string' },
        function: { body: '// args[0], args[1], ...\nreturn args[0];' },
      }
      const newNode: Node = {
        id,
        type: typeMap[type] ?? 'config',
        position: { x: position.x, y: position.y },
        data: dataMap[type] ?? {},
      }
      setNodes((nds) => nds.concat(newNode))
      lastClickRef.current = null
      setContextTarget(null)
    },
    [rfInstance, setNodes]
  )

  const deleteNode = React.useCallback((id: string | undefined) => {
    if (!id) return
    setNodes((nds) => nds.filter((n) => n.id !== id))
    setEdges((eds) => eds.filter((e) => e.source !== id && e.target !== id))
    setContextTarget(null)
  }, [setNodes, setEdges])

  return (
    <div className="reactflow-wrapper" ref={wrapperRef} onContextMenu={onCanvasContextMenu} style={{ position: 'relative' }}>
      <FlowContext.Provider value={{ nodes, setNodes, edges, setEdges }}>
        <ContextMenu>
          <ContextMenuTrigger asChild>
            <div style={{ width: '100%', height: '100%' }}>
              <ReactFlow
                nodes={nodes}
                edges={edges}
                onNodesChange={onNodesChange}
                onEdgesChange={onEdgesChange}
                onConnect={onConnect}
                nodeTypes={nodeTypes}
                fitView
                onInit={onInit}
              >
                <Background />
                <Controls />
                <MiniMap />
              </ReactFlow>
            </div>
          </ContextMenuTrigger>

          <ContextMenuContent>
            {contextTarget?.type === 'node' ? (
              <ContextMenuItem onSelect={() => deleteNode(contextTarget.nodeId)}>Delete</ContextMenuItem>
            ) : (
              <>
                <ContextMenuGroup>
                  <ContextMenuLabel>New Node</ContextMenuLabel>
                  <ContextMenuItem onSelect={() => createNode('config')}>Config</ContextMenuItem>
                  <ContextMenuItem onSelect={() => createNode('render')}>Renderer</ContextMenuItem>
                  <ContextMenuItem onSelect={() => createNode('variable')}>Variable</ContextMenuItem>
                  <ContextMenuItem onSelect={() => createNode('function')}>Function</ContextMenuItem>
                </ContextMenuGroup>
              </>
            )}
          </ContextMenuContent>
        </ContextMenu>
      </FlowContext.Provider>
    </div>
  )
}


