"use client";
import { useState, useEffect, useRef, Suspense } from "react";
import Secure from "../components/Secure/Secure";
import Alert from "@components/Alert/Alert";
import Header from "@components/Header/Header";
import { SystemNotificationsProvider } from "@components/SystemNotificationsProvider/SystemNotificationsProvider";
import UserMenu from "@components/UserMenu/UserMenu";
import style from "./style.module.css";
import useWindowSize from "../hooks/WindowSize";
import classNames from "classnames";
import Textbox from "../components/Textbox/Textbox";
import {
    FindConversationItem,
    IMessage,
    ISendMessageResult,
    MESSAGE_CONVERSATION_TYPES,
    SendMessageParams,
} from "@core/Models/Messages.model";
import {
    markConversationAsRead,
    messagesFind,
    messagesFindContacts,
    messagesFindConversations,
    sendMessage,
} from "./Messages.service";
import { useSessionStore } from "@core/Store/Sesion.Store";
import Avatar from "../components/Avatar/Avatar";
import SendIcon from "@mui/icons-material/Send";
import BackIcon from "@mui/icons-material/ArrowBack";
import { motion } from "motion/react";
import { IconButton } from "@mui/material";
import { EVENT_TYPES, useEventHandlerStore } from "@core/Store/EventHandler.Store";
import { useAlert } from "@core/Store/Alert.Store";
import { getChatClientSocket } from "./Messages.socket";
import { Socket } from "socket.io-client";
import dayjs from "dayjs";

import "dayjs/locale/es";
import { blurElementsOnLoad } from "@core/helpers/blur";
import HeaderConfProvider from "../components/HeaderConfProvider/HeaderConfProvider";
import SuspenseLoading from "../components/SuspendeLoading/SuspenseLoading";

dayjs.locale("es");

/**
 * TODO:
 * En la columna de conversaciones, vamos a poner en la parte superior una caja de texto
 * que permita buscar organizaciones, y en el caso de las organizaciones que el usuario es cliente
 * se tienen que mostrar los colaboradores de esa organizacion.
 * Si el usuario es colaborador de la organizacion, se tienen que mostrar los clientes de esa organizacion
 * y los otros colaboradores de la organizacion.
 */

enum VIEW_FOCUS {
    CONVERSATIONS = "CONVERSATIONS",
    MESSAGES = "MESSAGES",
}

enum MESSENGER_VIEWS {
    CONVERSATIONS = "CONVERSATIONS",
    MESSAGES = "MESSAGES",
    BOTH = "BOTH",
}

type ViewClasses =
    | "MessengerContainer"
    | "MessengerBody"
    | "ConversationsContainer"
    | "ConversationsHeader"
    | "ConversationsBody"
    | "MessagesContainer"
    | "MessagesHeader"
    | "MessagesBody";

const classesMap = {
    CONVERSATIONS: {
        MessengerContainer: style.MessengerContainer_CONVERSATIONS,
        MessengerBody: style.MessengerBody_CONVERSATIONS,
        ConversationsContainer: style.ConversationsContainer_CONVERSATIONS,
        ConversationsHeader: style.ConversationsHeader_CONVERSATIONS,
        ConversationsBody: style.ConversationsBody_CONVERSATIONS,
        MessagesContainer: style.MessagesContainer_CONVERSATIONS,
        MessagesHeader: style.MessagesHeader_CONVERSATIONS,
        MessagesBody: style.MessagesBody_CONVERSATIONS,
    },
    MESSAGES: {
        MessengerContainer: style.MessengerContainer_MESSAGES,
        MessengerBody: style.MessengerBody_MESSAGES,
        ConversationsContainer: style.ConversationsContainer_MESSAGES,
        ConversationsHeader: style.ConversationsHeader_MESSAGES,
        ConversationsBody: style.ConversationsBody_MESSAGES,
        MessagesContainer: style.MessagesContainer_MESSAGES,
        MessagesHeader: style.MessagesHeader_MESSAGES,
        MessagesBody: style.MessagesBody_MESSAGES,
    },
    BOTH: {
        MessengerContainer: style.MessengerContainer_BOTH,
        MessengerBody: style.MessengerBody_BOTH,
        ConversationsContainer: style.ConversationsContainer_BOTH,
        ConversationsHeader: style.ConversationsHeader_BOTH,
        ConversationsBody: style.ConversationsBody_BOTH,
        MessagesContainer: style.MessagesContainer_BOTH,
        MessagesHeader: style.MessagesHeader_BOTH,
        MessagesBody: style.MessagesBody_BOTH,
    },
};

interface FindConversationParams {
    selectConversationId?: string;
    page?: number;
}

export default function Messages() {
    const SessionInfo = useSessionStore();
    const alert = useAlert();
    const eventHandler = useEventHandlerStore();
    const [width, height] = useWindowSize();
    const [view, setView] = useState<MESSENGER_VIEWS>(MESSENGER_VIEWS.BOTH);
    const [viewFocus, setViewFocus] = useState<VIEW_FOCUS>(VIEW_FOCUS.CONVERSATIONS);
    const [conversations, setConversations] = useState<FindConversationItem[]>([]);
    const [messages, setMessages] = useState<IMessage[]>([]);
    const [selectedConversation, setSelectedConversation] = useState<
        FindConversationItem | undefined
    >(undefined);
    const [filter, setFilter] = useState<string>("");
    const [chatText, setChatText] = useState<string>("");
    const chatBodyRef = useRef<HTMLDivElement>(null);
    const [chatSocket, setChatSocket] = useState<Socket | undefined>(undefined);
    const [newMessageReceived, setNewMessageReceived] = useState<ISendMessageResult | null>(null);

    const [loadingChatNextPage, setLoadingChatNextPage] = useState<boolean>(false);
    const [chatPage, setChatPage] = useState<number>(1);

    const getClassName = (name: ViewClasses) => {
        return classNames(style[name], classesMap[view][name]);
    };

    const getMessageClassName = (message: IMessage) => {
        if (message.userId === SessionInfo.userId) {
            return classNames(style.MessageContainerSender);
        } else {
            return classNames(style.MessageContainerReceiver);
        }
    };

    const viewManager = () => {
        if (width < 800) {
            if (viewFocus === VIEW_FOCUS.CONVERSATIONS) {
                setView(MESSENGER_VIEWS.CONVERSATIONS);
            } else if (viewFocus === VIEW_FOCUS.MESSAGES) {
                setView(MESSENGER_VIEWS.MESSAGES);
            }
        } else {
            setView(MESSENGER_VIEWS.BOTH);
        }
    };

    const findContacts = (query: string) => {
        messagesFindContacts({
            page: 1,
            query: query,
            sessionUser: SessionInfo.userId,
            limit: 20,
        })
            .then((result) => {
                setConversations(result.data);
            })
            .finally(() => {
                eventHandler.setEventType(EVENT_TYPES.SLEEP);
            });
    };

    const findConversations = ({ page = 1, selectConversationId = "" }: FindConversationParams) => {
        eventHandler.setEventType(EVENT_TYPES.LOADING);
        messagesFindConversations({
            page: page,
            sessionUser: SessionInfo.userId,
            limit: 20,
        })
            .then((result) => {
                setConversations(result.data);
                if (selectConversationId != "") {
                    setSelectedConversation(
                        result.data.find((item) => item.conversation.id === selectConversationId)
                    );
                }
            })
            .finally(() => {
                eventHandler.setEventType(EVENT_TYPES.SLEEP);
            });
    };

    const loadConversationChat = (markAsRead: boolean = false) => {
        if (!selectedConversation) {
            return;
        }

        if (!selectedConversation.conversation.id) {
            setMessages([]);
            return;
        }

        messagesFind({
            conversationId: selectedConversation.conversation.id,
            sessionUser: SessionInfo.userId,
            page: 1,
            limit: 20,
        })
            .then((result) => {
                const messages = result.data;
                const orderedMessages = messages.sort((a, b) => {
                    return new Date(a.messageDate).getTime() - new Date(b.messageDate).getTime();
                });
                setMessages(orderedMessages);
                setChatPage(1);
                if (markAsRead) {
                    setTimeout(() => {
                        markCurrentConversationAsReadHandler();
                        markConversationsAsReadHandler();
                    }, 2000);
                }
            })
            .finally(() => {
                chatScrollToEnd();
            });
    };

    const loadConversationChatNextPage = () => {
        if (!selectedConversation) {
            return;
        }

        if (!selectedConversation.conversation.id) {
            setMessages([]);
            return;
        }

        if (loadingChatNextPage) {
            return;
        }

        setLoadingChatNextPage(true);

        const chatBody = chatBodyRef.current;
        const previousScrollHeight = chatBody?.scrollHeight || 0;
        const previousScrollTop = chatBody?.scrollTop || 0;

        messagesFind({
            conversationId: selectedConversation.conversation.id,
            sessionUser: SessionInfo.userId,
            page: chatPage + 1,
            limit: 20,
        })
            .then((result) => {
                if (result.page > chatPage && result.data.length > 0) {
                    setChatPage(result.page);
                    const messagesResult = [...messages, ...result.data];
                    const orderedMessages = messagesResult.sort((a, b) => {
                        return (
                            new Date(a.messageDate).getTime() - new Date(b.messageDate).getTime()
                        );
                    });
                    setMessages(orderedMessages);
                    //Lleva dobre requestAnimationFrame porque sino no se entera del nuevo tamanio del div.
                    requestAnimationFrame(() => {
                        requestAnimationFrame(() => {
                            const chatBody = chatBodyRef.current;
                            if (chatBody) {
                                const newScrollHeight = chatBody.scrollHeight;
                                const delta = newScrollHeight - previousScrollHeight;
                                chatBody.scrollTop = previousScrollTop + delta;
                            }
                        });
                    });
                }
            })
            .finally(() => {
                setLoadingChatNextPage(false);
            });
    };

    const send = () => {
        if (!selectedConversation) {
            return;
        }

        eventHandler.setEventType(EVENT_TYPES.LOADING);

        const conversationType = selectedConversation.conversation.type;

        const sendMessageData: SendMessageParams = {
            conversationId: selectedConversation.conversation.id
                ? selectedConversation.conversation.id
                : undefined,
            type: conversationType,
            userIdFrom: SessionInfo.userId,
            userIdTo:
                conversationType == MESSAGE_CONVERSATION_TYPES.USER_TO_USER
                    ? selectedConversation.conversation.userIdTo
                    : undefined,
            companyId:
                conversationType == MESSAGE_CONVERSATION_TYPES.USER_TO_ORG
                    ? selectedConversation.conversation.companyId
                    : undefined,
            message: chatText,
            sessionUser: SessionInfo.userId,
        };

        sendMessage(sendMessageData)
            .then((result) => {
                if (!sendMessageData.conversationId) {
                    findConversations({
                        page: 1,
                        selectConversationId: result.conversation.id,
                    });
                    return;
                }

                loadConversationChat();
            })
            .catch((error) => {
                alert.showError(error.format());
            })
            .finally(() => {
                chatScrollToEnd();
                setChatText("");
                eventHandler.setEventType(EVENT_TYPES.SLEEP);
            });
    };

    const conversationSelectedHandler = (item: FindConversationItem) => {
        if (view != MESSENGER_VIEWS.BOTH) {
            setView(MESSENGER_VIEWS.MESSAGES);
            setViewFocus(VIEW_FOCUS.MESSAGES);
        }
        setSelectedConversation(item);
        setChatText("");
        setFilter("");
    };

    const chatScrollToEnd = () => {
        requestAnimationFrame(() => {
            const chatBody = chatBodyRef.current;
            if (chatBody) {
                chatBody.scrollTop = chatBody.scrollHeight;
            }
        });
    };

    const chatScrollHandler = () => {
        const chatBody = chatBodyRef.current;
        if (chatBody && chatBody.scrollTop <= 50) {
            loadConversationChatNextPage();
        }
    };

    const updateOtherConversationsReadedCounter = (data: ISendMessageResult) => {
        const conversation = conversations.find(
            (c) => c.conversation.id === data.message.conversationId
        );
        if (conversation) {
            const newMessagesCount = conversation.conversation.newMessages
                ? conversation.conversation.newMessages + 1
                : 1;
            conversation.conversation.newMessages = newMessagesCount;
            const indexConversation = conversations.indexOf(conversation);
            const conversationsUpdated = [...conversations];
            conversationsUpdated[indexConversation] = conversation;
            setConversations(conversationsUpdated);
        }
    };

    const newMessageReceivedHandler = (data: ISendMessageResult) => {
        if (!selectedConversation) {
            updateOtherConversationsReadedCounter(data);
            return;
        }

        if (selectedConversation.conversation.id != data.message.conversationId) {
            updateOtherConversationsReadedCounter(data);
            return;
        }

        const updatedMessages = [...messages, ...[data.message]];
        setMessages(updatedMessages);
        chatScrollToEnd();
    };

    const markConversationsAsReadHandler = () => {
        if (!selectedConversation || !selectedConversation.conversation.id) {
            return;
        }

        if (!SessionInfo.userId) {
            return;
        }

        markConversationAsRead({
            conversationId: selectedConversation.conversation.id,
            sessionUser: SessionInfo.userId,
        });
    };

    const markCurrentConversationAsReadHandler = () => {
        if (!selectedConversation || !selectedConversation.conversation.id) {
            return;
        }

        const conversation = conversations.find(
            (c) => c.conversation.id === selectedConversation.conversation.id
        );
        if (conversation) {
            conversation.conversation.newMessages = 0;
            const indexConversation = conversations.indexOf(conversation);
            const conversationsUpdated = [...conversations];
            conversationsUpdated[indexConversation] = conversation;
            setConversations(conversationsUpdated);
        }
    };

    useEffect(() => {
        eventHandler.setEventType(EVENT_TYPES.LOADING);
        setSelectedConversation(undefined);
        blurElementsOnLoad();
    }, []);

    useEffect(() => {
        if (!SessionInfo.userId) {
            return;
        }

        if (filter != "") {
            findContacts(filter);
            return;
        }

        findConversations({ page: 1 });
    }, [SessionInfo.userId, filter]);

    useEffect(() => {
        if (!SessionInfo.userId) {
            if (chatSocket) {
                chatSocket.off("new_message");
                chatSocket.disconnect();
            }
            return;
        }

        loadConversationChat(true);

        if (chatSocket) {
            return;
        }

        const socket = getChatClientSocket(SessionInfo.userId);
        setChatSocket(socket);
    }, [SessionInfo.userId, selectedConversation]);

    useEffect(() => {
        if (!chatSocket) {
            return;
        }

        chatSocket.on("new_message", (message: ISendMessageResult) => {
            setNewMessageReceived(message);
        });

        return () => {
            chatSocket.off("new_message");
        };
    }, [chatSocket]);

    useEffect(() => {
        if (!newMessageReceived) {
            return;
        }
        newMessageReceivedHandler(newMessageReceived);
    }, [newMessageReceived]);

    useEffect(() => {
        viewManager();
    }, [width, height]);
    return (
        <Suspense fallback={<SuspenseLoading />}>
            <div>
                <Header />
                <HeaderConfProvider />
                <SystemNotificationsProvider />
                <Secure>
                    <div className={getClassName("MessengerContainer")}>
                        <div className={getClassName("MessengerBody")}>
                            <div className={getClassName("ConversationsContainer")}>
                                <div className={getClassName("ConversationsHeader")}>
                                    <Textbox
                                        type="text"
                                        value={filter}
                                        width="100%"
                                        onChange={(e) => setFilter(e.target.value)}
                                    />
                                </div>
                                <div className={getClassName("ConversationsBody")}>
                                    {conversations.length === 0 && (
                                        <div className={style.NoContacts}>
                                            No hay conversaciones activas. Puede buscar contactos en
                                            el cuadro de busqueda superior.
                                        </div>
                                    )}
                                    {conversations.map((item, index) => {
                                        return (
                                            <motion.div
                                                key={"conversation." + index}
                                                className="w-64 h-40 rounded-2xl cursor-pointer"
                                                initial={{ backgroundColor: "var(--white)" }}
                                                whileHover={{
                                                    backgroundColor: "var(--gray-lighter)",
                                                    color: "var(--black)",
                                                }} // Azul claro en hover
                                                whileTap={{
                                                    backgroundColor: "var(--wine-dark)",
                                                    color: "white",
                                                }} // Azul más fuerte en tap
                                                transition={{ duration: 0.2 }}
                                            >
                                                <div
                                                    className={style.ConversationItem}
                                                    onClick={() => {
                                                        conversationSelectedHandler(item);
                                                    }}
                                                >
                                                    <div>
                                                        <Avatar
                                                            src={item.image}
                                                            alt={item.name}
                                                            size="medium"
                                                            border="none"
                                                        />
                                                    </div>
                                                    <div style={{ width: "100%" }}>
                                                        <div
                                                            className={style.ConversationItemTitle}
                                                        >
                                                            {item.name}
                                                        </div>
                                                        <div
                                                            className={
                                                                style.ConversationItemDescription
                                                            }
                                                        >
                                                            {item.description}
                                                        </div>
                                                    </div>
                                                    {item.conversation.newMessages > 0 && (
                                                        <div
                                                            className={
                                                                style.ConversationItemNewMessages
                                                            }
                                                        >
                                                            {item.conversation.newMessages > 0
                                                                ? item.conversation.newMessages
                                                                : "-1"}
                                                        </div>
                                                    )}
                                                </div>
                                            </motion.div>
                                        );
                                    })}
                                </div>
                            </div>
                            <div className={getClassName("MessagesContainer")}>
                                {!selectedConversation && (
                                    <div className={style.NoConversation}>
                                        Seleccione un conversación
                                    </div>
                                )}
                                {selectedConversation && (
                                    <>
                                        <div className={getClassName("MessagesHeader")}>
                                            {selectedConversation && (
                                                <div className={style.MessageHeaderLeft}>
                                                    {view === MESSENGER_VIEWS.MESSAGES && (
                                                        <div
                                                            style={{
                                                                display: "flex",
                                                                alignItems: "center",
                                                            }}
                                                        >
                                                            <IconButton
                                                                onClick={() => {
                                                                    setView(
                                                                        MESSENGER_VIEWS.CONVERSATIONS
                                                                    );
                                                                    setViewFocus(
                                                                        VIEW_FOCUS.CONVERSATIONS
                                                                    );
                                                                }}
                                                            >
                                                                <BackIcon />
                                                            </IconButton>
                                                        </div>
                                                    )}
                                                    <div>
                                                        <Avatar
                                                            src={selectedConversation.image}
                                                            alt={selectedConversation.name}
                                                            size="small"
                                                            border="none"
                                                        />
                                                    </div>
                                                    <div>
                                                        <div
                                                            className={style.MessageHeaderLeftTitle}
                                                        >
                                                            {selectedConversation.name}
                                                        </div>
                                                        <div
                                                            className={
                                                                style.MessageHeaderLeftDescription
                                                            }
                                                        >
                                                            {selectedConversation.description}
                                                        </div>
                                                    </div>
                                                </div>
                                            )}
                                        </div>
                                        <div
                                            ref={chatBodyRef}
                                            className={getClassName("MessagesBody")}
                                            onScroll={chatScrollHandler}
                                        >
                                            <div className={style.MessageChatContainer}>
                                                {messages.map((item, index) => {
                                                    const showDate =
                                                        index === 0 ||
                                                        !dayjs(item.messageDate)
                                                            .startOf("day")
                                                            .isSame(
                                                                dayjs(
                                                                    messages[
                                                                        index - 1
                                                                    ].messageDate.toString()
                                                                ).startOf("day")
                                                            );

                                                    return (
                                                        <div key={"message." + index}>
                                                            {showDate && (
                                                                <div
                                                                    className={
                                                                        style.MessageChatDateContainer
                                                                    }
                                                                >
                                                                    <div
                                                                        className={
                                                                            style.MessageChatDate
                                                                        }
                                                                    >
                                                                        {dayjs(
                                                                            item.messageDate
                                                                        ).format(
                                                                            "dddd DD [de] MMMM, YYYY"
                                                                        )}
                                                                    </div>
                                                                </div>
                                                            )}
                                                            <div
                                                                className={getMessageClassName(
                                                                    item
                                                                )}
                                                            >
                                                                <div
                                                                    key={"message." + index}
                                                                    className={
                                                                        style.MessageChatItem
                                                                    }
                                                                >
                                                                    <div>{item.message}</div>
                                                                    <div>
                                                                        {dayjs(
                                                                            item.messageDate
                                                                        ).format("HH:mm")}
                                                                    </div>
                                                                </div>
                                                            </div>
                                                        </div>
                                                    );
                                                })}
                                            </div>
                                        </div>
                                        <div>
                                            <div className={style.MessageBottomBar}>
                                                <Textbox
                                                    type="text"
                                                    value={chatText}
                                                    width="100%"
                                                    onChange={(e) => {
                                                        setChatText(e.target.value);
                                                    }}
                                                    onKeyUp={(e) => {
                                                        if (e.key === "Enter") {
                                                            send();
                                                        }
                                                    }}
                                                />
                                                <IconButton color="success" onClick={send}>
                                                    <SendIcon />
                                                </IconButton>
                                            </div>
                                        </div>
                                    </>
                                )}
                            </div>
                        </div>
                    </div>
                </Secure>

                <UserMenu />
                <Alert />
            </div>
        </Suspense>
    );
}
