'use client';

import { useState, useCallback, useRef } from 'react';
import { useEventChannel, type ChannelEvent } from './useEventChannel';

/**
 * Chat message from a user.
 */
export interface ChatMessage {
  id: string;
  content: string;
  userId: string;
  userName?: string;
  timestamp: string;
}

/**
 * AI response message.
 */
export interface AIMessage {
  id: string;
  content: string;
  provider: string;
  timestamp: string;
}

/**
 * In-progress AI streaming message.
 */
export interface AIStreamingMessage {
  streamId: string;
  content: string;
  isStreaming: boolean;
  timestamp: string;
}

/**
 * Online user in the room.
 */
export interface OnlineUser {
  id: string;
  name?: string;
  status: 'online' | 'away' | 'offline';
}

/**
 * Configuration for the chat hook.
 */
export interface UseChatConfig {
  /** API endpoint for sending messages (e.g., '/api/chat/messages') */
  endpoint: string;
  /** SSE endpoint for events (default: '/api/events') */
  sseEndpoint?: string;
  /** Channel to subscribe to (e.g., 'channel:room123') */
  channel: string;
  /** Current user ID */
  userId: string;
  /** Current user display name */
  userName?: string;
  /** Additional headers for POST requests */
  headers?: Record<string, string>;
}

/**
 * Result from the chat hook.
 */
export interface UseChatResult {
  /** List of chat messages */
  messages: ChatMessage[];
  /** List of AI responses */
  aiMessages: AIMessage[];
  /** Map of in-progress AI streams */
  streamingMessages: Map<string, AIStreamingMessage>;
  /** List of online users */
  onlineUsers: OnlineUser[];
  /** SSE connection state */
  connectionState: 'connecting' | 'connected' | 'disconnected' | 'error';
  /** Send a chat message */
  sendMessage: (content: string) => Promise<void>;
  /** Clear all messages */
  clearMessages: () => void;
}

/** Maximum message IDs to track for deduplication */
const MAX_SEEN_MESSAGES = 500;

/**
 * Chat hook using HTTP2 + SSE pattern.
 *
 * - Send messages: HTTP POST to endpoint
 * - Receive messages: SSE subscription to channel
 *
 * @example
 * ```tsx
 * const {
 *   messages,
 *   aiMessages,
 *   sendMessage,
 *   connectionState,
 * } = useChat({
 *   endpoint: '/api/chat/messages',
 *   channel: 'channel:room123',
 *   userId: currentUser.id,
 *   userName: currentUser.name,
 * });
 *
 * // Send a message (HTTP POST)
 * await sendMessage('Hello, world!');
 *
 * // Messages arrive via SSE automatically
 * return (
 *   <div>
 *     {messages.map(msg => (
 *       <ChatBubble key={msg.id} {...msg} />
 *     ))}
 *   </div>
 * );
 * ```
 */
export function useChat(config: UseChatConfig): UseChatResult {
  const {
    endpoint,
    sseEndpoint = '/api/events',
    channel,
    userId,
    userName,
    headers,
  } = config;

  const [messages, setMessages] = useState<ChatMessage[]>([]);
  const [aiMessages, setAIMessages] = useState<AIMessage[]>([]);
  const [streamingMessages, setStreamingMessages] = useState<Map<string, AIStreamingMessage>>(new Map());
  const [onlineUsers, setOnlineUsers] = useState<OnlineUser[]>([]);

  const seenMessageIds = useRef(new Set<string>());

  // Handle incoming SSE events
  const handleEvent = useCallback((event: ChannelEvent) => {
    const eventId = event.id as string | undefined;

    switch (event.type) {
      case 'chat': {
        // Deduplicate
        if (eventId && seenMessageIds.current.has(eventId)) return;
        if (eventId) {
          seenMessageIds.current.add(eventId);
          if (seenMessageIds.current.size > MAX_SEEN_MESSAGES) {
            const arr = Array.from(seenMessageIds.current);
            seenMessageIds.current = new Set(arr.slice(-MAX_SEEN_MESSAGES));
          }
        }

        // Chat data may be at top level or nested in event.result (SSEEvent envelope)
        const payload = (event.result as Record<string, unknown>) || event;
        const msg: ChatMessage = {
          id: (payload.id as string) || eventId || crypto.randomUUID(),
          content: (payload.content as string) || (event.message as string) || '',
          userId: (payload.userId as string) || 'unknown',
          userName: payload.userName as string | undefined,
          timestamp: (payload.timestamp as string) || event.timestamp || new Date().toISOString(),
        };
        setMessages((prev) => [...prev, msg]);
        break;
      }

      case 'ai_chat_chunk': {
        // Chunk data may be at top level or nested in event.result (SSEEvent envelope)
        const chunkPayload = (event.result as Record<string, unknown>) || event;
        const streamId = (chunkPayload.streamId as string) || (event.streamId as string);
        const text = (chunkPayload.text as string) || (event.text as string) || '';
        const done = (chunkPayload.done as boolean) || (event.done as boolean) || false;

        setStreamingMessages((prev) => {
          const updated = new Map(prev);
          const existing = updated.get(streamId);

          if (done) {
            // Move completed stream to aiMessages
            const finalContent = (existing?.content || '') + text;
            if (finalContent) {
              const provider = (chunkPayload.provider as string) || '';
              setAIMessages((prevAI) => [...prevAI, {
                id: streamId || crypto.randomUUID(),
                content: finalContent,
                provider,
                timestamp: existing?.timestamp || new Date().toISOString(),
              }]);
            }
            updated.delete(streamId);
          } else {
            updated.set(streamId, {
              streamId,
              content: (existing?.content || '') + text,
              isStreaming: true,
              timestamp: existing?.timestamp || new Date().toISOString(),
            });
          }
          return updated;
        });
        break;
      }

      case 'ai_chat': {
        // AI chat data may be at top level or nested in event.result (SSEEvent envelope)
        const aiPayload = (event.result as Record<string, unknown>) || event;
        const aiId = (aiPayload.id as string) || event.jobId || eventId || crypto.randomUUID();

        // Deduplicate
        if (seenMessageIds.current.has(aiId)) return;
        seenMessageIds.current.add(aiId);
        if (seenMessageIds.current.size > MAX_SEEN_MESSAGES) {
          const arr = Array.from(seenMessageIds.current);
          seenMessageIds.current = new Set(arr.slice(-MAX_SEEN_MESSAGES));
        }

        const aiMsg: AIMessage = {
          id: aiId,
          content: (aiPayload.content as string) || (event.message as string) || '',
          provider: (aiPayload.provider as string) || '',
          timestamp: (aiPayload.timestamp as string) || event.timestamp || new Date().toISOString(),
        };
        setAIMessages((prev) => [...prev, aiMsg]);
        break;
      }

      case 'presence': {
        const presenceUserId = event.userId as string;
        const status = event.status as 'online' | 'away' | 'offline';
        const name = event.userName as string | undefined;

        setOnlineUsers((prev) => {
          if (status === 'offline') {
            return prev.filter((u) => u.id !== presenceUserId);
          }
          const existing = prev.find((u) => u.id === presenceUserId);
          if (existing) {
            return prev.map((u) =>
              u.id === presenceUserId ? { ...u, status, name } : u
            );
          }
          return [...prev, { id: presenceUserId, name, status }];
        });
        break;
      }
    }
  }, []);

  // Subscribe to SSE channel
  const { state: sseState } = useEventChannel({
    endpoint: sseEndpoint,
    channel,
    onEvent: handleEvent,
  });

  // Send message via HTTP POST
  const sendMessage = useCallback(
    async (content: string) => {
      const response = await fetch(endpoint, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          ...headers,
        },
        body: JSON.stringify({
          content,
          userId,
          userName,
          channel,
        }),
      });

      if (!response.ok) {
        const error = await response.json().catch(() => ({}));
        throw new Error((error as { message?: string }).message || `HTTP ${response.status}`);
      }
    },
    [endpoint, headers, userId, userName, channel]
  );

  const clearMessages = useCallback(() => {
    setMessages([]);
    setAIMessages([]);
    setStreamingMessages(new Map());
    seenMessageIds.current.clear();
  }, []);

  return {
    messages,
    aiMessages,
    streamingMessages,
    onlineUsers,
    connectionState: sseState,
    sendMessage,
    clearMessages,
  };
}
