import { useState, useCallback, useRef } from 'react' import { editorAIApi } from '@/api/editorAI' import { analytics } from '@/lib/analytics' import type { AIActionType, EditorAIChatMessage, AISuggestion, ContextMenuPosition, } from '@/types' interface UseEditorAIOptions { flowType: 'troubleshooting' | 'procedural' treeId?: string | null /** Returns the live flow structure from the editor for AI context */ getFlowContext?: () => Record | null /** Called when the AI response contains a working_tree update */ onFlowUpdate?: (workingTree: Record, metadata?: Record | null) => void } export function useEditorAI({ flowType, treeId, getFlowContext, onFlowUpdate }: UseEditorAIOptions) { const [isOpen, setIsOpen] = useState(false) const [focalNodeId, setFocalNodeId] = useState(null) const [contextMenu, setContextMenu] = useState<{ position: ContextMenuPosition nodeId: string } | null>(null) const [sessionId, setSessionId] = useState(null) const [messages, setMessages] = useState([]) const [input, setInput] = useState('') const [isLoading, setIsLoading] = useState(false) const [suggestions, setSuggestions] = useState([]) const pendingActionRef = useRef('open_chat') const ensureSession = useCallback(async () => { if (sessionId) return sessionId try { const result = await editorAIApi.startSession(flowType, treeId || undefined) setSessionId(result.session_id) if (result.greeting) { setMessages((prev) => [ ...prev, { role: 'assistant' as const, content: result.greeting, timestamp: new Date().toISOString(), }, ]) } return result.session_id } catch { return null } }, [sessionId, flowType, treeId]) const openPanel = useCallback((nodeId?: string, actionType?: AIActionType) => { setIsOpen(true) if (nodeId) setFocalNodeId(nodeId) if (actionType) pendingActionRef.current = actionType }, []) const closePanel = useCallback(() => { setIsOpen(false) setContextMenu(null) }, []) const openContextMenu = useCallback((e: React.MouseEvent, nodeId: string) => { e.preventDefault() e.stopPropagation() setContextMenu({ position: { x: e.clientX, y: e.clientY }, nodeId }) }, []) const closeContextMenu = useCallback(() => { setContextMenu(null) }, []) const sendMessage = useCallback(async () => { if (!input.trim() || isLoading) return const currentInput = input const currentAction = pendingActionRef.current const currentFocalNodeId = focalNodeId const userMessage: EditorAIChatMessage = { role: 'user', content: currentInput, timestamp: new Date().toISOString(), action_type: currentAction, } setMessages((prev) => [...prev, userMessage]) setInput('') setIsLoading(true) try { const sid = await ensureSession() if (!sid) return const result = await editorAIApi.sendMessage({ sessionId: sid, content: currentInput, actionType: currentAction, focalNodeId: currentFocalNodeId, flowContext: getFlowContext?.() || null, }) analytics.aiFeatureUsed({ feature: 'flow_assist' }) setMessages((prev) => [ ...prev, { role: 'assistant', content: result.content, timestamp: new Date().toISOString(), }, ]) // Apply AI-generated flow structure to the editor if (result.working_tree && onFlowUpdate) { onFlowUpdate(result.working_tree, result.tree_metadata || null) } } catch { setMessages((prev) => [ ...prev, { role: 'assistant', content: 'Sorry, something went wrong. Please try again.', timestamp: new Date().toISOString(), }, ]) } finally { setIsLoading(false) pendingActionRef.current = 'open_chat' } }, [input, isLoading, ensureSession, focalNodeId, getFlowContext, onFlowUpdate]) const triggerAction = useCallback( (nodeId: string, actionType: AIActionType, prompt: string) => { setFocalNodeId(nodeId) pendingActionRef.current = actionType setInput(prompt) setIsOpen(true) }, [] ) return { isOpen, openPanel, closePanel, focalNodeId, setFocalNodeId, contextMenu, openContextMenu, closeContextMenu, messages, input, setInput, sendMessage, isLoading, suggestions, setSuggestions, triggerAction, } }