import { useState, useEffect, useCallback, useRef } from 'react'; interface RealTimeDataOptions { strategyId: number; reconnectInterval?: number; maxReconnectAttempts?: number; onConnect?: () => void; onDisconnect?: () => void; onError?: (error: Event) => void; onMessage?: (data: any) => void; } interface RealTimeDataState { data: any; isConnected: boolean; isConnecting: boolean; error: string | null; reconnectAttempts: number; } export const useRealTimeData = (options: RealTimeDataOptions) => { const { strategyId, reconnectInterval = 5000, maxReconnectAttempts = 5, onConnect, onDisconnect, onError, onMessage } = options; const [state, setState] = useState({ data: null, isConnected: false, isConnecting: false, error: null, reconnectAttempts: 0 }); const wsRef = useRef(null); const reconnectTimeoutRef = useRef(null); const reconnectAttemptsRef = useRef(0); const connect = useCallback(() => { if (wsRef.current?.readyState === WebSocket.OPEN) { return; } setState(prev => ({ ...prev, isConnecting: true, error: null })); try { // For development, use a mock WebSocket connection // In production, this would be the actual WebSocket URL const wsUrl = process.env.NODE_ENV === 'development' ? `ws://localhost:8000/ws/strategy/${strategyId}/live` : `wss://api.alwrity.com/ws/strategy/${strategyId}/live`; const ws = new WebSocket(wsUrl); wsRef.current = ws; ws.onopen = () => { setState(prev => ({ ...prev, isConnected: true, isConnecting: false, error: null, reconnectAttempts: 0 })); reconnectAttemptsRef.current = 0; onConnect?.(); }; ws.onmessage = (event) => { try { const data = JSON.parse(event.data); setState(prev => ({ ...prev, data })); onMessage?.(data); } catch (error) { console.error('Error parsing WebSocket message:', error); } }; ws.onclose = (event) => { setState(prev => ({ ...prev, isConnected: false, isConnecting: false })); onDisconnect?.(); // Attempt to reconnect if not a clean close if (event.code !== 1000 && reconnectAttemptsRef.current < maxReconnectAttempts) { reconnectAttemptsRef.current += 1; setState(prev => ({ ...prev, reconnectAttempts: reconnectAttemptsRef.current })); reconnectTimeoutRef.current = setTimeout(() => { connect(); }, reconnectInterval); } }; ws.onerror = (error) => { setState(prev => ({ ...prev, error: 'WebSocket connection error', isConnecting: false })); onError?.(error); }; } catch (error) { setState(prev => ({ ...prev, error: 'Failed to create WebSocket connection', isConnecting: false })); } }, [strategyId, reconnectInterval, maxReconnectAttempts, onConnect, onDisconnect, onError, onMessage]); const disconnect = useCallback(() => { if (reconnectTimeoutRef.current) { clearTimeout(reconnectTimeoutRef.current); reconnectTimeoutRef.current = null; } if (wsRef.current) { wsRef.current.close(1000); // Clean close wsRef.current = null; } setState(prev => ({ ...prev, isConnected: false, isConnecting: false, reconnectAttempts: 0 })); }, []); const sendMessage = useCallback((message: any) => { if (wsRef.current?.readyState === WebSocket.OPEN) { wsRef.current.send(JSON.stringify(message)); } else { console.warn('WebSocket is not connected'); } }, []); // Connect on mount and when strategyId changes useEffect(() => { connect(); return () => { disconnect(); }; }, [connect, disconnect]); // Cleanup on unmount useEffect(() => { return () => { disconnect(); }; }, [disconnect]); return { ...state, connect, disconnect, sendMessage }; }; // Mock real-time data for development export const useMockRealTimeData = (strategyId: number) => { const [data, setData] = useState(null); useEffect(() => { // Simulate real-time data updates const interval = setInterval(() => { const mockData = { timestamp: new Date().toISOString(), strategyId, metrics: { traffic_growth: Math.random() * 20 + 5, // 5-25% engagement_rate: Math.random() * 10 + 5, // 5-15% conversion_rate: Math.random() * 3 + 1, // 1-4% content_quality_score: Math.random() * 20 + 80, // 80-100% strategy_adoption_rate: Math.random() * 20 + 80, // 80-100% roi_ratio: Math.random() * 2 + 2, // 2-4x competitive_position_rank: Math.floor(Math.random() * 5) + 1, // 1-5 audience_growth_percentage: Math.random() * 15 + 8, // 8-23% confidence_score: Math.random() * 20 + 80 // 80-100% }, alerts: Math.random() > 0.8 ? [ { type: 'warning', message: 'Engagement rate dropped below target', timestamp: new Date().toISOString() } ] : [], trends: { daily: Array.from({ length: 7 }, (_, i) => ({ date: new Date(Date.now() - (6 - i) * 24 * 60 * 60 * 1000).toISOString().split('T')[0], traffic_growth: Math.random() * 20 + 5, engagement_rate: Math.random() * 10 + 5, conversion_rate: Math.random() * 3 + 1, content_quality_score: Math.random() * 20 + 80 })) } }; setData(mockData); }, 5000); // Update every 5 seconds return () => clearInterval(interval); }, [strategyId]); return { data, isConnected: true, isConnecting: false, error: null, reconnectAttempts: 0, connect: () => {}, disconnect: () => {}, sendMessage: () => {} }; }; // Export both hooks export default useRealTimeData;