/*
 * Runtime Web – Unified Serverless Framework for Full-Stack TypeScript Applications
 * Documentation: https://runtimeweb.com/docs/getting-started
 *
 * @template: ai-chat
 */

/**
 * useChat Hook
 *
 * React hook for managing chat state, message sending, and error handling.
 * Supports streaming responses with automatic fallback to non-streaming mode.
 *
 * @module hooks/useChat
 */

import { useState, useCallback } from 'react';
import { chatApiService } from '../services';
import type { Message, BehaviorMode } from '../types/ui.types';
import type { ChatRequest } from '../types/chat.types';

/**
 * Return type for the useChat hook.
 */
export interface UseChatReturn {
    /** Array of all messages in the current conversation */
    messages: Message[];
    /** Whether a request is currently in progress */
    isLoading: boolean;
    /** Error from the last failed request, if any */
    error: Error | null;
    /** Sends a new message with the specified behavior mode */
    sendMessage: (content: string, behavior: BehaviorMode) => Promise<void>;
    /** Retries the last failed message with the same content and behavior */
    retryLastMessage: () => Promise<void>;
    /** Clears the current error state */
    clearError: () => void;
}

/**
 * Hook for managing chat state and message operations.
 *
 * This hook provides complete chat functionality including:
 * - Message state management (user and assistant messages)
 * - Loading state during API requests
 * - Error handling with retry capability
 * - Streaming response support with automatic fallback
 *
 * @param sessionId - The unique session identifier for the conversation
 * @returns An object containing chat state and operations
 *
 * @example
 * ```typescript
 * import { useChat } from '../hooks/useChat';
 * import { useSession } from '../hooks/useSession';
 *
 * function ChatPage() {
 *   const { sessionId } = useSession();
 *   const {
 *     messages,
 *     isLoading,
 *     error,
 *     sendMessage,
 *     retryLastMessage,
 *     clearError,
 *   } = useChat(sessionId);
 *
 *   const handleSend = async (content: string) => {
 *     await sendMessage(content, 'balanced');
 *   };
 *
 *   return (
 *     <div>
 *       <MessageList messages={messages} />
 *       {error && <ErrorMessage error={error} onRetry={retryLastMessage} />}
 *       <InputArea onSend={handleSend} disabled={isLoading} />
 *     </div>
 *   );
 * }
 * ```
 *
 * **Validates: Requirements 4.1, 4.3, 6.1, 6.3, 6.6**
 */
export function useChat(sessionId: string): UseChatReturn {
    const [messages, setMessages] = useState<Message[]>([]);
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState<Error | null>(null);
    const [lastRequest, setLastRequest] = useState<ChatRequest | null>(null);

    /**
     * Sends a message to the AI backend with streaming support.
     * Attempts streaming first, falls back to non-streaming on failure.
     *
     * @param content - The message content to send
     * @param behavior - The behavior mode for AI response generation
     */
    const sendMessage = useCallback(async (content: string, behavior: BehaviorMode) => {
        const userMessage: Message = {
            id: `msg-${Date.now()}`,
            role: 'user',
            content,
            timestamp: new Date().toISOString(),
        };

        setMessages((prev) => [...prev, userMessage]);
        setIsLoading(true);
        setError(null);

        const request: ChatRequest = {
            sessionId,
            message: content,
            behavior,
        };
        setLastRequest(request);

        try {
            // Try streaming first, fall back to non-streaming
            let assistantContent = '';
            const assistantId = `msg-${Date.now() + 1}`;

            try {
                // Add placeholder for streaming message
                setMessages((prev) => [...prev, {
                    id: assistantId,
                    role: 'assistant',
                    content: '',
                    timestamp: new Date().toISOString(),
                    isStreaming: true,
                }]);

                for await (const chunk of chatApiService.sendMessageStream(request)) {
                    assistantContent += chunk.token || '';
                    setMessages((prev) => prev.map((msg) =>
                        msg.id === assistantId
                            ? { ...msg, content: assistantContent }
                            : msg
                    ));
                }

                // Check if we got any content - if not, fall back to non-streaming
                if (!assistantContent.trim()) {
                    throw new Error('Empty streaming response');
                }

                // Finalize streaming message - mark as complete
                setMessages((prev) => prev.map((msg) =>
                    msg.id === assistantId
                        ? { ...msg, content: assistantContent, isStreaming: false }
                        : msg
                ));
            } catch (streamError) {
                // Remove streaming placeholder before fallback
                setMessages((prev) => prev.filter((msg) => msg.id !== assistantId));

                // Fall back to non-streaming
                const response = await chatApiService.sendMessage(request);

                setMessages((prev) => [...prev, {
                    id: assistantId,
                    role: 'assistant',
                    content: response.response,
                    timestamp: response.timestamp,
                    isStreaming: false,
                }]);
            }
        } catch (err) {
            setError(err instanceof Error ? err : new Error('Unknown error'));
            // Remove the user message on error so they can retry
            setMessages((prev) => prev.filter((msg) => msg.id !== userMessage.id));
        } finally {
            setIsLoading(false);
        }
    }, [sessionId]);

    /**
     * Retries the last failed message with the original content and behavior.
     * Does nothing if there is no previous request to retry.
     */
    const retryLastMessage = useCallback(async () => {
        if (lastRequest) {
            await sendMessage(lastRequest.message, lastRequest.behavior as BehaviorMode);
        }
    }, [lastRequest, sendMessage]);

    /**
     * Clears the current error state.
     * Useful for dismissing error messages without retrying.
     */
    const clearError = useCallback(() => {
        setError(null);
    }, []);

    return {
        messages,
        isLoading,
        error,
        sendMessage,
        retryLastMessage,
        clearError,
    };
}
