import { ChatContext } from '@/context/chat/ChatContext.tsx';
import { ReactNode, useState } from 'react';
import { ChatMessagesProps, ContactsProps, MeProps } from '@/types/types.ts';
import { joinRoom } from '@/socket/socket.ts';
import { getMessages, setContactStatus } from '@/api/contactsApi.tsx';
import axios from 'axios';

export const ChatProvider = ({ children }: { children: ReactNode }) => {
  const [contactsList, setContactsList] = useState<ContactsProps[]>([]);
  const [currentContact, setCurrentContact] = useState<ContactsProps | null>(
    null,
  );
  const [cursor, setCursor] = useState<number>(0);
  const [messages, setMessages] = useState<ChatMessagesProps[]>([]);
  const [errorMessage, setErrorMessage] = useState<string | null>(null);
  const [hasMoreMessages, setHasMoreMessages] = useState<boolean>(true);
  const [me, setMe] = useState<MeProps>({
    isGroupAdmin: false,
    isGroupOwner: false,
  });
  const [groupOwner, setGroupOwner] = useState<string | undefined>();

  async function initializeContact(newContact: ContactsProps) {
    setMessages([]); // Clear messages from previous contact
    localStorage.setItem('contact', JSON.stringify(newContact)); // Set current contact in localstorage
    setCurrentContact(newContact);
    console.log('Initialized contact: ', newContact);
    try {
      const joinResult = await joinRoom(newContact.conversation_id);
      if (!joinResult.success) {
        setErrorMessage(joinResult.message);
        return false;
      }
      try {
        await fetchMessages(newContact.conversation_id);
      } catch (e) {
        console.error('Failed to fetch messages: ', e);
        return false;
      }
      setContactsList((prevContacts) =>
        prevContacts.map((c) =>
          c.username === newContact.username ? { ...c, read: true } : c,
        ),
      );
      console.log('Current contact is now: ', newContact);
      return true;
    } catch (e) {
      console.error('Failed to initialize contact:', e);
      return false;
    }
  }

  const fetchMessages = async (conversation_id: string) => {
    console.log('Fetching messages for: ', conversation_id);
    try {
      const messages = await getMessages(conversation_id);
      if (messages.messages.length < 50) {
        setHasMoreMessages(false);
        setErrorMessage('No more messages found');
      }

      setCursor(() => {
        return Math.min(
          ...messages.messages.map((message) => message.message_id),
        );
      });

      messages.messages.forEach(messageHandler);
    } catch (e) {
      if (axios.isAxiosError(e)) {
        setErrorMessage(e.message);
      }
    }
  };

  function messageHandler(msg: ChatMessagesProps) {
    setMessages((prevMessages) => {
      // Check if the message already exists in the state
      if (!prevMessages.some((m) => m.message_id === msg.message_id)) {
        // Convert sent_at to Date object before adding to state
        const messageWithDate = {
          ...msg,
          sent_at: new Date(msg.sent_at),
        };
        return [...prevMessages, messageWithDate];
      }
      return prevMessages;
    });
  }

  function updateContactStatus(contactObj: ContactsProps, read: boolean) {
    console.log('Update contact status: ', contactObj);

    setContactsList((prevContacts) =>
      prevContacts.map((contact) => {
        if (contact.conversation_id === contactObj.conversation_id) {
          if (!contactObj.read) {
            setContactStatus(contactObj.conversation_id, read);
          }
          return { ...contact, read: read };
        } else {
          return contact;
        }
      }),
    );
  }

  const fetchPreviousMessages = async (contact: string | null) => {
    if (!hasMoreMessages) {
      return;
    }

    console.log(
      'Fetching messages for: ',
      contact,
      'with cursor: ',
      cursor,
      'hasmoremessages: ',
      hasMoreMessages,
    );

    try {
      const messages = await getMessages(contact, cursor, 50);
      if (messages.messages.length < 50) {
        setHasMoreMessages(false);
        setErrorMessage('No more messages found');
      }
      messages.messages.forEach((msg) => {
        setMessages((prevMessages) => {
          if (!prevMessages.some((m) => m.message_id === msg.message_id)) {
            const messageWithDate = {
              ...msg,
              sent_at: new Date(msg.sent_at),
            };

            return [messageWithDate, ...prevMessages];
          }
          return prevMessages;
        });
      });

      setCursor(() => {
        return Math.min(
          ...messages.messages.map((message) => message.message_id),
        );
      });
    } catch (e) {
      if (axios.isAxiosError(e)) {
        setErrorMessage(e.message);
      }
    }
  };

  const value = {
    contactsList,
    currentContact,
    cursor,
    messages,
    errorMessage,
    hasMoreMessages,
    me,
    groupOwner,

    setContactsList,
    setCurrentContact,
    setCursor,
    setMessages,
    setErrorMessage,
    setHasMoreMessages,
    setMe,
    setGroupOwner,

    initializeContact,
    fetchMessages,
    messageHandler,
    updateContactStatus,
    fetchPreviousMessages,
  };

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