import { useState, useEffect, useRef } from 'react'; import { ApiResponse, ChatMessage, UserProfile } from '../../interface'; import { CHAT_ENDPOINT } from '../../endpoint'; export function useChatInterface(userProfile: UserProfile, isProfileComplete?: boolean) { const [chatHistory, setChatHistory] = useState([]); const [isLoading, setIsLoading] = useState(false); const [currentMessage, setCurrentMessage] = useState(''); const [showPlanRecs, setShowPlanRecs] = useState(false); const chatEndRef = useRef(null); const inputRef = useRef(null); // threadId is generated once per chat session and persists for the hook's lifetime const [threadId] = useState(() => crypto.randomUUID()); useEffect(() => { chatEndRef.current?.scrollIntoView({ behavior: 'smooth' }); // Focus input after chatHistory changes (i.e., after response is rendered) if (inputRef.current) { inputRef.current.focus(); } }, [chatHistory]); // Prevent double API call: only send initial chat message once when userProfile is complete const hasSentInitialMessage = useRef(false); useEffect(() => { // TODO: review this logic // Check if userProfile is complete before sending initial message const isValidProfile = !!userProfile && userProfile?.zip_code?.length && userProfile?.zip_code?.length > 0 && !!userProfile.age && userProfile.age > 0 && !!userProfile.gender && userProfile.gender.length > 0 && !!userProfile.household_size && !!userProfile.income && !!userProfile.employment_status && !!userProfile.citizenship; if (isValidProfile && !hasSentInitialMessage.current) { sendChatMessage('START_PROFILE_BUILDING', userProfile); hasSentInitialMessage.current = true; } }, [userProfile]); const sendChatMessage = async ( message: string, profileOverride?: UserProfile ) => { setIsLoading(true); try { const payload = { thread_id: threadId, user_profile: profileOverride ?? userProfile, message, is_profile_complete: isProfileComplete ?? false, conversation_history: chatHistory.map(m => `${m.role}: ${m.content}`) }; const response = await fetch(CHAT_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!response.ok) throw new Error('Failed to send message'); const data: ApiResponse = await response.json(); // Only append the agent's message to the existing chatHistory const agentMsgRaw = data.updated_history[data.updated_history.length - 1] || ''; const agentMsgContent = agentMsgRaw.replace(/^Agent:\s*/, ''); const agentMessage: ChatMessage = { role: 'agent', content: agentMsgContent, timestamp: Date.now(), plans: !showPlanRecs && data.plan_recommendations?.recommendations?.length && data.plan_recommendations?.recommendations?.length > 0 ? data.plan_recommendations.recommendations : undefined }; setChatHistory(prev => [...prev, agentMessage]); if (!showPlanRecs && data.plan_recommendations?.recommendations?.length && data.plan_recommendations?.recommendations?.length > 0) { setShowPlanRecs(true); } return data; } catch (error) { console.error('Error sending message:', error); } finally { setIsLoading(false); } }; const handleSendMessage = async () => { if (!currentMessage.trim()) return; // Add user message to chat immediately const userMessage: ChatMessage = { role: 'user', content: currentMessage, timestamp: Date.now() }; setChatHistory(prev => [...prev, userMessage]); const messageToSend = currentMessage; setCurrentMessage(''); await sendChatMessage(messageToSend); }; return { chatHistory, isLoading, currentMessage, setCurrentMessage, handleSendMessage, showPlanRecs, chatEndRef, inputRef, sendChatMessage }; }