import { useContext, useEffect, useRef, useState, useCallback } from 'react';
import { socket } from '@/socket/socket.ts';
import { sendContact } from '@/api/contactsApi.tsx';
import LoadingWheel from '../LoadingWheel.tsx';
import AnimatedMessage from '@/components/chat/chatArea/MessageElement.tsx';
import { ChatMessagesProps } from '@/types/types.ts';
import { useChat } from '@/context/chat/useChat.ts';
import { AuthContext } from '@/utils/AuthProvider.tsx';
import { useWindowFocus } from '@/hooks/useWindowFocus.tsx';

function MessagesArea() {
  const {
    messages,
    setMessages,
    currentContact,
    messageHandler,
    setContactsList,
    errorMessage,
    fetchPreviousMessages,
    contactsList,
    setCurrentContact,
  } = useChat();
  const containerRef = useRef<HTMLDivElement | null>(null);
  const { user } = useContext(AuthContext);
  const [isLoading, setIsLoading] = useState<boolean>(false);
  const [shouldScrollToBottom, setShouldScrollToBottom] = useState(true);
  const previousMessagesLength = useRef<number>(messages.length);
  const isFocused = useWindowFocus();
  const previousTitle = useRef<string>(document.title);

  const scrollToBottom = () => {
    const container = containerRef.current;
    if (container && shouldScrollToBottom) {
      container.scrollTop = container.scrollHeight;
    }
  };

  const handleScroll = useCallback(async () => {
    const container = containerRef.current;
    if (!container || isLoading) return;

    if (container.scrollTop === 0) {
      setIsLoading(true);
      try {
        const previousScrollHeight = container.scrollHeight;
        await fetchPreviousMessages(currentContact!.conversation_id);
        // Adjust scroll position after fetching new messages
        container.scrollTop = container.scrollHeight - previousScrollHeight;
      } catch (e) {
        console.error('Error fetching previous messages:', e);
      } finally {
        setIsLoading(false);
      }
    }

    const isAtBottom =
      container.scrollHeight - container.scrollTop <=
      container.clientHeight + 200;
    setShouldScrollToBottom(isAtBottom);
  }, [isLoading, currentContact, fetchPreviousMessages]);

  useEffect(() => {
    const currentContainer = containerRef.current;
    if (!currentContainer) return;

    currentContainer.addEventListener('scroll', handleScroll);

    return () => {
      currentContainer.removeEventListener('scroll', handleScroll);
    };
  }, [handleScroll]);

  useEffect(() => {
    if (!socket) return;
    socket.on('chat message', (msg: ChatMessagesProps) => {
      const isFromCurrentConversation =
        msg.conversation_id === currentContact?.conversation_id;
      const isFromCurrentUser = msg.sender === user?.username;

      if (isFromCurrentConversation) {
        messageHandler(msg);
        if (!isFocused) {
          setContactsList((prevContacts) =>
            prevContacts.map((contact) =>
              contact.conversation_id === currentContact?.conversation_id
                ? { ...contact, read: false }
                : contact,
            ),
          );
          document.title = 'New message ❗';
        } else {
          socket?.emit('read message', {
            conversation_id: msg.conversation_id,
            message_id: msg.message_id,
          });
        }
      }

      setContactsList((prevContacts) => {
        const isFromAnotherConversation =
          !isFromCurrentConversation && !isFromCurrentUser;
        const existingContact = prevContacts.find(
          (c) => c.conversation_id === msg.conversation_id,
        );

        if (isFromAnotherConversation && !existingContact) {
          sendContact(msg.sender);
          return [
            ...prevContacts,
            {
              read: false,
              username: msg.sender,
              user_id: msg.sender_id,
              conversation_id: msg.conversation_id,
              type: 'direct',
              id: msg.message_id,
              last_active: new Date().toISOString(),
              last_message: msg.message,
              last_message_id: msg.message_id,
              last_message_sender: msg.sender,
              last_message_time: new Date().toISOString(),
              last_read_message_id: prevContacts[0]?.last_message_id || null,
            },
          ];
        }

        return prevContacts.map((contact) =>
          contact.conversation_id === msg.conversation_id
            ? {
                ...contact,
                read: isFromCurrentConversation ? isFocused : false,
                last_active: new Date().toString(),
                last_message: msg.message,
                last_message_sender: msg.sender,
                last_message_id: msg.message_id,
                last_message_time: new Date().toString(),
              }
            : contact,
        );
      });
    });

    socket.on(
      'delete message',
      (msg: { conversation_id: string; message_id: number }) => {
        setMessages((prevMessages) =>
          prevMessages.filter(
            (message) => message.message_id !== msg.message_id,
          ),
        );
        setContactsList((prevContacts) =>
          prevContacts.map((contact) =>
            contact.last_message_id === msg.message_id
              ? {
                  ...contact,
                  last_active: new Date().toString(),
                  last_message: 'message deleted',
                  last_message_time: new Date().toString(),
                }
              : contact,
          ),
        );
      },
    );

    return () => {
      if (!socket) return;
      socket.off('chat message');
      socket.off('delete message');
    };
  }, [currentContact, user?.username, setContactsList, isFocused]);

  useEffect(() => {
    if (!currentContact) return;
    const currentContactOnList = contactsList.find(
      (contact) => contact.conversation_id === currentContact.conversation_id,
    );
    if (currentContactOnList && messages.length > 1) {
      socket?.emit('message read', {
        conversation_id: currentContact.conversation_id,
        message_id: messages[messages.length - 1].message_id,
      });
      setCurrentContact({ ...currentContact, read: currentContactOnList.read });
    }
  }, [contactsList, currentContact?.conversation_id]);

  useEffect(() => {
    if (currentContact?.read) return;
    document.title = previousTitle.current;
    if (messages.length > 1) {
      socket?.emit('message read', {
        conversation_id: currentContact?.conversation_id,
        message_id: messages[messages.length - 1].message_id,
      });
    }

    const timeout = setTimeout(() => {
      setContactsList((prevContacts) =>
        prevContacts.map((contact) =>
          contact.conversation_id === currentContact?.conversation_id
            ? { ...contact, read: true }
            : contact,
        ),
      );
    }, 800);

    return () => clearTimeout(timeout);
  }, [isFocused]);

  useEffect(() => {
    const hasNewMessages = messages.length > previousMessagesLength.current;
    previousMessagesLength.current = messages.length;

    if (hasNewMessages && shouldScrollToBottom) {
      scrollToBottom();
      setTimeout(scrollToBottom, 100);
    }
  }, [messages, shouldScrollToBottom]);

  useEffect(() => {
    scrollToBottom();
  }, []);

  const deleteMessage = async (message_id: number) => {
    try {
      socket?.emit(
        'delete message',
        {
          message_id,
          conversation_id: currentContact?.conversation_id,
        },
        (response: { status: string; message: string }) => {
          if (response.status !== 'ok') {
            console.error('Failed to delete message: ', response.message);
          }
        },
      );
    } catch (e) {
      if (e instanceof Error) {
        console.error('Failed to delete message:', e);
      }
    }
  };

  return (
    <div ref={containerRef} className="flex flex-col h-full overflow-y-auto">
      <p className="text-center text-gray-400">{errorMessage}</p>
      <ul className="flex-grow list-none">
        {isLoading ? <LoadingWheel /> : null}
        {messages.map((msg: ChatMessagesProps) => (
          <AnimatedMessage
            key={msg.message_id}
            message={msg}
            onDelete={() => deleteMessage(msg.message_id)}
          />
        ))}
      </ul>
    </div>
  );
}

export default MessagesArea;
