import { useState, useCallback, useEffect, useRef } from 'react';
import axios from 'axios';
import {
  ChatBoxState,
  ChatConversation,
  ChatMessage,
  ChatUser,
  UseChatConversationsReturn,
} from '../types/chat';

const STORAGE_KEY = 'zeiterfassung_open_chats';
const MAX_MESSAGES_PER_CONVERSATION = 100;

interface UseChatConversationsProps {
  currentUserId?: number;
  maxVisibleChats?: number;
  wsConnected: boolean;
  wsSendMessage: (conversationId: string, content: string) => void;
  wsCreateAndSend: (recipientUserId: number, content: string) => Promise<{ success: boolean; conversation?: any; message?: any; error?: string }>;
  wsJoinConversation: (conversationId: string) => void;
  wsMarkAsRead: (conversationId: string) => void;
  chatServerUrl?: string;
  chatToken?: string;
}

// Extended chat box state to handle pending conversations
interface ExtendedChatBoxState extends ChatBoxState {
  pendingUserId?: number; // User ID when conversation doesn't exist yet
  pendingUserName?: string;
  pendingUserAvatar?: string | null;
}

export const useChatConversations = ({
  currentUserId,
  maxVisibleChats = 3,
  wsConnected,
  wsSendMessage,
  wsCreateAndSend,
  wsJoinConversation,
  wsMarkAsRead,
  chatServerUrl,
  chatToken,
}: UseChatConversationsProps): UseChatConversationsReturn & {
  handleIncomingMessage: (message: ChatMessage) => void;
  loadConversation: (conversationId: string) => Promise<ChatConversation | null>;
  handleConversationCreated: (conversationId: string, recipientUserId: number) => void;
  loadMoreMessages: (conversationId: string) => Promise<void>;
  hasMoreMessages: (conversationId: string) => boolean;
} => {
  const [openChats, setOpenChats] = useState<ExtendedChatBoxState[]>([]);
  const [messages, setMessages] = useState<Record<string, ChatMessage[]>>({});
  const [loadingMessages, setLoadingMessages] = useState<Record<string, boolean>>({});

  const messageIdCounter = useRef(0);

  // Load persisted open chats from localStorage on mount
  useEffect(() => {
    try {
      const saved = localStorage.getItem(STORAGE_KEY);
      if (saved) {
        const parsed = JSON.parse(saved);
        // Only restore conversation IDs and minimized state, positions will be recalculated
        // Skip pending conversations (they don't persist)
        const validChats = parsed.filter((chat: any) => chat.conversationId && !chat.conversationId.startsWith('pending_'));
        setOpenChats(
          validChats.map((chat: any, index: number) => ({
            conversationId: chat.conversationId,
            conversation: null, // Will be loaded fresh
            minimized: chat.minimized ?? false,
            position: index,
            unreadCount: 0,
          }))
        );
      }
    } catch (err) {
      console.error('[Chat Conversations] Failed to load saved chats:', err);
    }
  }, []);

  // Persist open chats to localStorage
  useEffect(() => {
    try {
      // Don't persist pending conversations
      const toSave = openChats
        .filter((chat) => !chat.conversationId.startsWith('pending_'))
        .map((chat) => ({
          conversationId: chat.conversationId,
          minimized: chat.minimized,
        }));
      localStorage.setItem(STORAGE_KEY, JSON.stringify(toSave));
    } catch (err) {
      console.error('[Chat Conversations] Failed to save chats:', err);
    }
  }, [openChats]);

  // Track if we've loaded persisted conversations to avoid re-loading
  const persistedChatsLoadedRef = useRef(false);

  // Ref to track if we have more messages to load (for pagination)
  const hasMoreMessagesRef = useRef<Record<string, boolean>>({});
  const loadingMoreRef = useRef<Record<string, boolean>>({});

  // Load messages from the messages endpoint
  const loadMessages = useCallback(
    async (conversationId: string, cursor?: string): Promise<{ messages: ChatMessage[]; nextCursor: string | null }> => {
      if (!chatServerUrl || !chatToken) {
        return { messages: [], nextCursor: null };
      }

      try {
        let url = `${chatServerUrl}/api/messages/conversation/${conversationId}?limit=50`;
        if (cursor) {
          url += `&cursor=${cursor}`;
        }

        const response = await axios.get(url, {
          headers: { Authorization: `Bearer ${chatToken}` },
        });

        const data = response.data;
        const messages: ChatMessage[] = (data.messages || []).map((m: any) => ({
          id: m.id,
          conversationId: conversationId,
          senderId: parseInt(m.senderId || m.sender?.externalUserId, 10),
          content: m.content,
          timestamp: m.createdAt || m.timestamp,
          status: 'delivered',
          type: (m.type || 'text').toLowerCase(),
        }));

        // Sort messages chronologically (oldest first) for proper display order
        messages.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());

        return {
          messages,
          nextCursor: data.nextCursor || null,
        };
      } catch (err) {
        console.error('[Chat Conversations] Failed to load messages:', err);
        return { messages: [], nextCursor: null };
      }
    },
    [chatServerUrl, chatToken]
  );

  // Load conversation details and messages from chat server directly
  const loadConversation = useCallback(
    async (conversationId: string): Promise<ChatConversation | null> => {
      if (!chatServerUrl || !chatToken) {
        console.error('[Chat Conversations] No chat server URL or token available');
        return null;
      }

      try {
        const response = await axios.get(`${chatServerUrl}/api/conversations/${conversationId}`, {
          headers: { Authorization: `Bearer ${chatToken}` },
        });
        const data = response.data;

        // Map API response to ChatConversation
        // Note: We don't use avatar from chat server as it may not be accurate
        const conversation: ChatConversation = {
          id: data.id,
          type: data.type?.toLowerCase() === 'group' ? 'group' : 'direct',
          name: data.name,
          participants: (data.participants || []).map((p: any) => ({
            id: parseInt(p.externalUserId || p.id, 10),
            name: p.name || 'Unknown',
            avatar: null, // Don't use avatar from chat server - will be fetched from local users
            isOnline: p.isOnline ?? false,
          })),
          lastMessage: data.lastMessage
            ? {
                id: data.lastMessage.id,
                conversationId: conversationId,
                senderId: parseInt(data.lastMessage.senderId, 10),
                content: data.lastMessage.content,
                timestamp: data.lastMessage.createdAt || data.lastMessage.timestamp,
                status: 'delivered',
                type: 'text',
              }
            : undefined,
          unreadCount: data.unreadCount || 0,
          createdAt: data.createdAt,
          updatedAt: data.updatedAt,
        };

        // Load messages from the messages endpoint
        const { messages: loadedMessages, nextCursor } = await loadMessages(conversationId);
        hasMoreMessagesRef.current[conversationId] = !!nextCursor;

        if (loadedMessages.length > 0) {
          setMessages((prev) => ({
            ...prev,
            [conversationId]: loadedMessages,
          }));
        }

        return conversation;
      } catch (err) {
        console.error('[Chat Conversations] Failed to load conversation:', err);
        return null;
      }
    },
    [chatServerUrl, chatToken, loadMessages]
  );

  // Load conversation details for persisted chats once we have server URL and token
  useEffect(() => {
    // Only run once when we have the required credentials
    if (!chatServerUrl || !chatToken || persistedChatsLoadedRef.current) {
      return;
    }

    // Find chats that were restored from localStorage but don't have conversation details
    const chatsToLoad = openChats.filter(
      (chat) => !chat.conversation && !chat.conversationId.startsWith('pending_')
    );

    if (chatsToLoad.length === 0) {
      return;
    }

    console.log('[Chat Conversations] Loading persisted conversations:', chatsToLoad.length);
    persistedChatsLoadedRef.current = true;

    // Load each conversation's details
    chatsToLoad.forEach(async (chat) => {
      setLoadingMessages((prev) => ({ ...prev, [chat.conversationId]: true }));

      const conversation = await loadConversation(chat.conversationId);

      setLoadingMessages((prev) => ({ ...prev, [chat.conversationId]: false }));

      if (conversation) {
        setOpenChats((prev) =>
          prev.map((c) =>
            c.conversationId === chat.conversationId ? { ...c, conversation } : c
          )
        );

        // Mark as read if not minimized
        if (!chat.minimized) {
          wsMarkAsRead(chat.conversationId);
        }
      } else {
        // Conversation not found (maybe deleted), remove it
        console.warn('[Chat Conversations] Persisted conversation not found, removing:', chat.conversationId);
        setOpenChats((prev) => prev.filter((c) => c.conversationId !== chat.conversationId));
      }
    });
  }, [chatServerUrl, chatToken, openChats, loadConversation, wsMarkAsRead]);

  // Load more messages (for infinite scroll)
  const loadMoreMessages = useCallback(
    async (conversationId: string): Promise<void> => {
      if (!chatServerUrl || !chatToken) return;
      if (loadingMoreRef.current[conversationId]) return;
      if (!hasMoreMessagesRef.current[conversationId]) return;

      const currentMessages = messages[conversationId] || [];
      if (currentMessages.length === 0) return;

      // Get the oldest message ID as cursor
      const oldestMessage = currentMessages[0];
      const cursor = oldestMessage.id;

      loadingMoreRef.current[conversationId] = true;

      try {
        const { messages: olderMessages, nextCursor } = await loadMessages(conversationId, cursor);
        hasMoreMessagesRef.current[conversationId] = !!nextCursor;

        if (olderMessages.length > 0) {
          setMessages((prev) => ({
            ...prev,
            [conversationId]: [...olderMessages, ...(prev[conversationId] || [])],
          }));
        }
      } finally {
        loadingMoreRef.current[conversationId] = false;
      }
    },
    [chatServerUrl, chatToken, loadMessages, messages]
  );

  // Expand chat function declaration (hoisted for use in openChat)
  const expandChat = useCallback(
    (conversationId: string) => {
      // Mark messages as read when expanding (opening) the conversation
      if (!conversationId.startsWith('pending_')) {
        wsMarkAsRead(conversationId);
      }

      setOpenChats((prev) => {
        const chatIndex = prev.findIndex((c) => c.conversationId === conversationId);
        if (chatIndex === -1) return prev;

        const chat = prev[chatIndex];
        if (!chat.minimized) return prev; // Already expanded

        // Move to position 0 (rightmost) and expand
        const others = prev.filter((_, i) => i !== chatIndex);
        const updated = others.map((c, i) => ({
          ...c,
          position: i + 1,
          // Auto-minimize others if exceeding max
          minimized: i >= maxVisibleChats - 1 ? true : c.minimized,
        }));

        return [{ ...chat, minimized: false, position: 0, unreadCount: 0 }, ...updated];
      });
    },
    [maxVisibleChats, wsMarkAsRead]
  );

  // Open a chat with a user
  const openChat = useCallback(
    async (userId: number, existingConversationId?: string, userName?: string, userAvatar?: string | null): Promise<void> => {
      // Check if we already have a chat with this user (by conversation ID or pending user ID)
      const existingChat = openChats.find(
        (chat) =>
          chat.conversationId === existingConversationId ||
          chat.pendingUserId === userId ||
          (chat.conversation?.participants.some((p) => p.id === userId) && chat.conversation.type === 'direct')
      );

      if (existingChat) {
        // Already open, just expand it
        expandChat(existingChat.conversationId);
        return;
      }

      if (existingConversationId) {
        // Existing conversation - load it
        // Note: Server auto-joins user to all their conversation rooms on WebSocket connect
        setLoadingMessages((prev) => ({ ...prev, [existingConversationId]: true }));
        const conversation = await loadConversation(existingConversationId);
        setLoadingMessages((prev) => ({ ...prev, [existingConversationId]: false }));

        // Mark messages as read when opening the conversation
        wsMarkAsRead(existingConversationId);

        // Add new chat box
        setOpenChats((prev) => {
          // Calculate positions - new chat goes to position 0 (rightmost)
          const updated = prev.map((chat) => ({
            ...chat,
            position: chat.position + 1,
            // Auto-minimize if exceeding max visible
            minimized: chat.position >= maxVisibleChats - 1 ? true : chat.minimized,
          }));

          return [
            {
              conversationId: existingConversationId,
              conversation,
              minimized: false,
              position: 0,
              unreadCount: 0,
            },
            ...updated,
          ];
        });
      } else {
        // New conversation - create a pending chat box
        const pendingId = `pending_${userId}_${Date.now()}`;

        // We need user info for the header - fetch it if not provided
        let displayName = userName;
        let displayAvatar = userAvatar;

        if (!displayName) {
          try {
            const usersResponse = await axios.get('/zeiterfassung/chat/users');
            const user = usersResponse.data.find((u: any) => u.id === userId);
            if (user) {
              displayName = user.name;
              displayAvatar = user.avatar;
            }
          } catch (err) {
            console.error('[Chat] Failed to fetch user info:', err);
          }
        }

        // Create a temporary conversation object for display
        const pendingConversation: ChatConversation = {
          id: pendingId,
          type: 'direct',
          participants: [
            {
              id: userId,
              name: displayName || `User ${userId}`,
              avatar: displayAvatar ?? null,
              isOnline: false,
            },
          ],
          unreadCount: 0,
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString(),
        };

        setOpenChats((prev) => {
          const updated = prev.map((chat) => ({
            ...chat,
            position: chat.position + 1,
            minimized: chat.position >= maxVisibleChats - 1 ? true : chat.minimized,
          }));

          return [
            {
              conversationId: pendingId,
              conversation: pendingConversation,
              minimized: false,
              position: 0,
              unreadCount: 0,
              pendingUserId: userId,
              pendingUserName: displayName,
              pendingUserAvatar: displayAvatar,
            } as ExtendedChatBoxState,
            ...updated,
          ];
        });

        // Initialize empty messages for this pending conversation
        setMessages((prev) => ({
          ...prev,
          [pendingId]: [],
        }));
      }
    },
    [openChats, loadConversation, maxVisibleChats, expandChat]
  );

  // Handle when a conversation is created (from WebSocket response)
  const handleConversationCreated = useCallback(
    (conversationId: string, recipientUserId: number) => {
      console.log('[Chat] Conversation created:', conversationId, 'for user:', recipientUserId);

      // Join the newly created conversation room (like Flutter joinConversation)
      wsJoinConversation(conversationId);

      setOpenChats((prev) => {
        // Find the pending chat for this user
        const pendingIndex = prev.findIndex((chat) => chat.pendingUserId === recipientUserId);

        if (pendingIndex === -1) return prev;

        const pendingChat = prev[pendingIndex];
        const pendingId = pendingChat.conversationId;

        // Move messages from pending to real conversation
        setMessages((prevMessages) => {
          const pendingMessages = prevMessages[pendingId] || [];
          const { [pendingId]: _, ...rest } = prevMessages;
          return {
            ...rest,
            [conversationId]: pendingMessages.map((m) => ({
              ...m,
              conversationId,
            })),
          };
        });

        // Update the chat box with real conversation ID
        const updated = [...prev];
        updated[pendingIndex] = {
          ...pendingChat,
          conversationId,
          pendingUserId: undefined,
          pendingUserName: undefined,
          pendingUserAvatar: undefined,
        };

        return updated;
      });

      // Load full conversation details
      loadConversation(conversationId).then((conversation) => {
        if (conversation) {
          setOpenChats((prev) =>
            prev.map((chat) =>
              chat.conversationId === conversationId ? { ...chat, conversation } : chat
            )
          );
        }
      });
    },
    [loadConversation, wsJoinConversation]
  );

  // Close a chat
  const closeChat = useCallback((conversationId: string) => {
    // Note: Server manages room membership automatically, no need to manually leave

    setOpenChats((prev) => {
      const filtered = prev.filter((c) => c.conversationId !== conversationId);
      // Recalculate positions
      return filtered.map((chat, index) => ({
        ...chat,
        position: index,
      }));
    });

    // Clean up messages
    setMessages((prev) => {
      const { [conversationId]: _, ...rest } = prev;
      return rest;
    });
  }, []);

  // Minimize a chat
  const minimizeChat = useCallback((conversationId: string) => {
    setOpenChats((prev) =>
      prev.map((chat) =>
        chat.conversationId === conversationId ? { ...chat, minimized: true } : chat
      )
    );
  }, []);

  // Send a message
  const sendMessage = useCallback(
    async (conversationId: string, content: string) => {
      if (!content.trim() || !currentUserId) return;

      // Check if this is a pending conversation
      const chatState = openChats.find((c) => c.conversationId === conversationId) as
        | ExtendedChatBoxState
        | undefined;

      if (chatState?.pendingUserId) {
        // This is a new conversation - use createAndSend
        console.log('[Chat] Creating conversation and sending to user:', chatState.pendingUserId);

        // Create optimistic message
        const tempId = `temp_${Date.now()}_${messageIdCounter.current++}`;
        const optimisticMessage: ChatMessage = {
          id: tempId,
          conversationId,
          senderId: currentUserId,
          content: content.trim(),
          timestamp: new Date().toISOString(),
          status: 'sending',
          type: 'text',
        };

        // Add to local messages
        setMessages((prev) => ({
          ...prev,
          [conversationId]: [
            ...(prev[conversationId] || []),
            optimisticMessage,
          ].slice(-MAX_MESSAGES_PER_CONVERSATION),
        }));

        // Send via WebSocket and get the response with callback
        if (wsConnected) {
          const response = await wsCreateAndSend(chatState.pendingUserId, content.trim());

          if (response.success && response.conversation) {
            const newConversationId = response.conversation.id;
            console.log('[Chat] Conversation created:', newConversationId);

            // Join the new conversation room
            wsJoinConversation(newConversationId);

            // Move messages from pending to real conversation
            setMessages((prevMessages) => {
              const pendingMessages = prevMessages[conversationId] || [];
              const { [conversationId]: _, ...rest } = prevMessages;

              // Update messages with real conversation ID and mark as sent
              const updatedMessages = pendingMessages.map((m) =>
                m.id === tempId
                  ? {
                      ...m,
                      id: response.message?.id || m.id,
                      conversationId: newConversationId,
                      status: 'sent' as const,
                    }
                  : { ...m, conversationId: newConversationId }
              );

              return {
                ...rest,
                [newConversationId]: updatedMessages,
              };
            });

            // Update the chat box with real conversation
            setOpenChats((prev) => {
              const pendingIndex = prev.findIndex((c) => c.conversationId === conversationId);
              if (pendingIndex === -1) return prev;

              const updated = [...prev];

              // Map API response to ChatConversation
              const conversation: ChatConversation = {
                id: response.conversation.id,
                type: response.conversation.type?.toLowerCase() === 'group' ? 'group' : 'direct',
                name: response.conversation.name,
                participants: (response.conversation.participants || []).map((p: any) => ({
                  id: parseInt(p.externalUserId || p.id, 10),
                  name: p.name || 'Unknown',
                  avatar: null,
                  isOnline: p.isOnline ?? false,
                })),
                unreadCount: 0,
                createdAt: response.conversation.createdAt || new Date().toISOString(),
                updatedAt: response.conversation.updatedAt || new Date().toISOString(),
              };

              updated[pendingIndex] = {
                ...updated[pendingIndex],
                conversationId: newConversationId,
                conversation,
                pendingUserId: undefined,
                pendingUserName: undefined,
                pendingUserAvatar: undefined,
              };

              return updated;
            });
          } else {
            // Failed to create conversation
            console.error('[Chat] Failed to create conversation:', response.error);
            setMessages((prev) => ({
              ...prev,
              [conversationId]: (prev[conversationId] || []).map((m) =>
                m.id === tempId ? { ...m, status: 'failed' } : m
              ),
            }));
          }
        }

        return;
      }

      // Regular message to existing conversation
      const tempId = `temp_${Date.now()}_${messageIdCounter.current++}`;
      const optimisticMessage: ChatMessage = {
        id: tempId,
        conversationId,
        senderId: currentUserId,
        content: content.trim(),
        timestamp: new Date().toISOString(),
        status: 'sending',
        type: 'text',
      };

      // Add to local messages
      setMessages((prev) => ({
        ...prev,
        [conversationId]: [...(prev[conversationId] || []), optimisticMessage].slice(
          -MAX_MESSAGES_PER_CONVERSATION
        ),
      }));

      // Send via WebSocket if connected
      if (wsConnected) {
        wsSendMessage(conversationId, content.trim());

        // Mark as sent after a short delay
        setTimeout(() => {
          setMessages((prev) => ({
            ...prev,
            [conversationId]: (prev[conversationId] || []).map((m) =>
              m.id === tempId ? { ...m, status: 'sent' } : m
            ),
          }));
        }, 100);
      } else {
        // Mark as failed if not connected
        setMessages((prev) => ({
          ...prev,
          [conversationId]: (prev[conversationId] || []).map((m) =>
            m.id === tempId ? { ...m, status: 'failed' } : m
          ),
        }));
      }
    },
    [currentUserId, wsConnected, wsSendMessage, wsCreateAndSend, openChats]
  );

  // Handle incoming message from WebSocket
  const handleIncomingMessage = useCallback((message: ChatMessage) => {
    setMessages((prev) => {
      const existing = prev[message.conversationId] || [];

      // Check for duplicate (same ID or matching temp message)
      if (existing.some((m) => m.id === message.id)) {
        return prev;
      }

      // Replace temp message if this is confirmation of our own message
      const tempIndex = existing.findIndex(
        (m) =>
          m.status === 'sending' && m.senderId === message.senderId && m.content === message.content
      );

      if (tempIndex !== -1) {
        const updated = [...existing];
        updated[tempIndex] = { ...message, status: 'delivered' };
        return { ...prev, [message.conversationId]: updated };
      }

      // Add new message
      return {
        ...prev,
        [message.conversationId]: [...existing, message].slice(-MAX_MESSAGES_PER_CONVERSATION),
      };
    });

    // Update unread count if chat is minimized
    setOpenChats((prev) =>
      prev.map((chat) => {
        if (chat.conversationId === message.conversationId && chat.minimized) {
          return { ...chat, unreadCount: chat.unreadCount + 1 };
        }
        return chat;
      })
    );
  }, []);

  // Check if there are more messages for a conversation
  const hasMoreMessages = useCallback(
    (conversationId: string): boolean => {
      return hasMoreMessagesRef.current[conversationId] ?? false;
    },
    []
  );

  return {
    openChats: openChats as ChatBoxState[],
    openChat,
    closeChat,
    minimizeChat,
    expandChat,
    messages,
    loadingMessages,
    sendMessage,
    handleIncomingMessage,
    loadConversation,
    handleConversationCreated,
    loadMoreMessages,
    hasMoreMessages,
  };
};

export default useChatConversations;
