'use client';

import React, {
  createContext,
  useContext,
  useState,
  useCallback,
  useRef,
  ReactNode,
} from 'react';
import {
  ControlCenterConversation,
  ControlCenterMessage,
  StreamEvent,
  ToolCall,
  ToolResult,
} from '@/types/control-center';

// =============================================================================
// Types
// =============================================================================

/**
 * Summary of a conversation for the list view
 */
export interface ConversationSummary {
  id: string;
  title: string;
  createdAt: string;
  updatedAt: string;
  messageCount: number;
}

/**
 * Context state interface
 */
interface ChatContextState {
  /** List of conversation summaries */
  conversations: ConversationSummary[];
  /** Currently loaded conversation with full messages */
  currentConversation: ControlCenterConversation | null;
  /** Loading state for API calls */
  isLoading: boolean;
  /** Whether a response is currently streaming */
  isStreaming: boolean;
  /** Current streaming text content */
  streamingContent: string;
  /** Error message if any */
  error: string | null;
}

/**
 * Context actions interface
 */
interface ChatContextActions {
  /** Load all conversations from the API */
  loadConversations: () => Promise<void>;
  /** Select and load a conversation by ID */
  selectConversation: (id: string) => Promise<void>;
  /** Send a message to the current conversation */
  sendMessage: (message: string, provider?: string) => Promise<void>;
  /** Start a new conversation */
  newConversation: () => void;
  /** Clear the current error */
  clearError: () => void;
}

type ChatContextValue = ChatContextState & ChatContextActions;

// =============================================================================
// Context
// =============================================================================

const ChatContext = createContext<ChatContextValue | null>(null);

// =============================================================================
// Provider Component
// =============================================================================

interface ChatProviderProps {
  children: ReactNode;
}

export function ChatProvider({ children }: ChatProviderProps) {
  // State
  const [conversations, setConversations] = useState<ConversationSummary[]>([]);
  const [currentConversation, setCurrentConversation] = useState<ControlCenterConversation | null>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [isStreaming, setIsStreaming] = useState(false);
  const [streamingContent, setStreamingContent] = useState('');
  const [error, setError] = useState<string | null>(null);

  // Abort controller for cancelling streams
  const abortControllerRef = useRef<AbortController | null>(null);

  /**
   * Load all conversations from the API
   */
  const loadConversations = useCallback(async () => {
    setIsLoading(true);
    setError(null);

    try {
      const response = await fetch('/api/v1/control-center/conversations');

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        throw new Error(errorData.error || `Failed to load conversations: ${response.status}`);
      }

      const data = await response.json();
      setConversations(data.conversations || []);
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Failed to load conversations';
      setError(message);
      console.error('Error loading conversations:', err);
    } finally {
      setIsLoading(false);
    }
  }, []);

  /**
   * Select and load a conversation by ID
   */
  const selectConversation = useCallback(async (id: string) => {
    setIsLoading(true);
    setError(null);

    try {
      const response = await fetch(`/api/v1/control-center/history/${id}`);

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        throw new Error(errorData.error || `Failed to load conversation: ${response.status}`);
      }

      const data = await response.json();
      setCurrentConversation(data.conversation);
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Failed to load conversation';
      setError(message);
      console.error('Error loading conversation:', err);
    } finally {
      setIsLoading(false);
    }
  }, []);

  /**
   * Parse SSE data from a chunk
   */
  const parseSSEEvents = (chunk: string): StreamEvent[] => {
    const events: StreamEvent[] = [];
    const lines = chunk.split('\n');

    for (const line of lines) {
      const trimmedLine = line.trim();
      if (trimmedLine.startsWith('data:')) {
        const jsonStr = trimmedLine.slice(5).trim();
        if (jsonStr && jsonStr !== '[DONE]') {
          try {
            const parsed = JSON.parse(jsonStr);
            events.push(parsed);
          } catch (e) {
            console.warn('Failed to parse SSE event:', jsonStr);
          }
        }
      }
    }

    return events;
  };

  /**
   * Send a message to the current conversation with SSE streaming
   */
  const sendMessage = useCallback(async (message: string, provider?: string) => {
    // Cancel any existing stream
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
    }

    setIsLoading(true);
    setIsStreaming(true);
    setStreamingContent('');
    setError(null);

    // Create abort controller for this request
    abortControllerRef.current = new AbortController();

    // Add user message to conversation optimistically
    const userMessage: ControlCenterMessage = {
      id: `temp-${Date.now()}`,
      role: 'user',
      content: message,
      createdAt: new Date().toISOString(),
    };

    setCurrentConversation((prev) => {
      if (prev) {
        return {
          ...prev,
          messages: [...prev.messages, userMessage],
          updatedAt: new Date().toISOString(),
        };
      }
      // Create new conversation if none exists
      return {
        id: '',
        title: message.slice(0, 50) + (message.length > 50 ? '...' : ''),
        messages: [userMessage],
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString(),
      };
    });

    try {
      const response = await fetch('/api/v1/control-center/chat', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'text/event-stream',
        },
        body: JSON.stringify({
          conversationId: currentConversation?.id || undefined,
          message,
          provider: provider || 'openai',
        }),
        signal: abortControllerRef.current.signal,
      });

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        throw new Error(errorData.error || `Chat request failed: ${response.status}`);
      }

      if (!response.body) {
        throw new Error('No response body received');
      }

      // Process the stream
      const reader = response.body.getReader();
      const decoder = new TextDecoder();
      let buffer = '';
      let accumulatedContent = '';
      let conversationId = currentConversation?.id || '';
      let currentMessageId = '';
      const toolCalls: ToolCall[] = [];
      const toolResults: ToolResult[] = [];

      setIsLoading(false);

      while (true) {
        const { done, value } = await reader.read();

        if (done) {
          break;
        }

        // Decode the chunk and add to buffer
        buffer += decoder.decode(value, { stream: true });

        // Split by double newline to get complete events
        const eventChunks = buffer.split('\n\n');
        buffer = eventChunks.pop() || ''; // Keep incomplete chunk in buffer

        for (const chunk of eventChunks) {
          const events = parseSSEEvents(chunk);

          for (const event of events) {
            switch (event.type) {
              case 'message_start':
                conversationId = event.conversationId;
                currentMessageId = event.messageId;
                // Update conversation ID if it was empty
                if (!currentConversation?.id) {
                  setCurrentConversation((prev) => prev ? { ...prev, id: conversationId } : prev);
                }
                break;

              case 'content_delta':
                accumulatedContent += event.delta;
                setStreamingContent(accumulatedContent);
                break;

              case 'tool_call_start':
                toolCalls.push(event.toolCall);
                break;

              case 'tool_result':
                toolResults.push(event.toolResult);
                break;

              case 'message_complete':
                // Replace streaming content with final message
                setCurrentConversation((prev) => {
                  if (!prev) return prev;

                  // Update the user message ID if it was temporary
                  const updatedMessages = prev.messages.map((msg) => {
                    if (msg.id.startsWith('temp-')) {
                      return { ...msg, id: `user-${Date.now()}` };
                    }
                    return msg;
                  });

                  // Add the assistant message
                  return {
                    ...prev,
                    id: conversationId || prev.id,
                    messages: [...updatedMessages, event.message],
                    updatedAt: new Date().toISOString(),
                  };
                });
                setStreamingContent('');
                setIsStreaming(false);
                break;

              case 'error':
                throw new Error(event.error);
            }
          }
        }
      }

      // Handle case where stream ends without message_complete
      if (isStreaming && accumulatedContent) {
        const assistantMessage: ControlCenterMessage = {
          id: currentMessageId || `assistant-${Date.now()}`,
          role: 'assistant',
          content: accumulatedContent,
          toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
          toolResults: toolResults.length > 0 ? toolResults : undefined,
          createdAt: new Date().toISOString(),
        };

        setCurrentConversation((prev) => {
          if (!prev) return prev;
          return {
            ...prev,
            id: conversationId || prev.id,
            messages: [...prev.messages, assistantMessage],
            updatedAt: new Date().toISOString(),
          };
        });
      }
    } catch (err) {
      if (err instanceof Error && err.name === 'AbortError') {
        // Request was aborted, don't treat as error
        return;
      }

      const message = err instanceof Error ? err.message : 'Failed to send message';
      setError(message);
      console.error('Error sending message:', err);

      // Remove the optimistically added user message on error
      setCurrentConversation((prev) => {
        if (!prev) return prev;
        return {
          ...prev,
          messages: prev.messages.filter((msg) => !msg.id.startsWith('temp-')),
        };
      });
    } finally {
      setIsLoading(false);
      setIsStreaming(false);
      setStreamingContent('');
      abortControllerRef.current = null;
    }
  }, [currentConversation?.id]);

  /**
   * Start a new conversation
   */
  const newConversation = useCallback(() => {
    // Cancel any existing stream
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
    }

    setCurrentConversation(null);
    setStreamingContent('');
    setIsStreaming(false);
    setError(null);
  }, []);

  /**
   * Clear the current error
   */
  const clearError = useCallback(() => {
    setError(null);
  }, []);

  // Context value
  const value: ChatContextValue = {
    // State
    conversations,
    currentConversation,
    isLoading,
    isStreaming,
    streamingContent,
    error,
    // Actions
    loadConversations,
    selectConversation,
    sendMessage,
    newConversation,
    clearError,
  };

  return (
    <ChatContext.Provider value={value}>
      {children}
    </ChatContext.Provider>
  );
}

// =============================================================================
// Hook
// =============================================================================

/**
 * Hook to access the chat context
 * Must be used within a ChatProvider
 */
export function useChatContext(): ChatContextValue {
  const context = useContext(ChatContext);

  if (!context) {
    throw new Error('useChatContext must be used within a ChatProvider');
  }

  return context;
}

export default ChatProvider;
