import { useNavigate, useParams } from "react-router-dom";
import styles from "./styles.module.css";
import { ChangeEvent, useCallback, useEffect, useRef, useState } from "react";
import { Assistants, useApi, useApiCall } from "@/api";
import { useSelector } from "react-redux";
import { selectors } from "@/store";
import { IAssistant } from "@/api/resources/Assistants";
import Loader, { LoaderColor } from "@/components/Loader";
import ChatHeader from "./components/ChatHeader";
import InputMessage from "./components/InputMessage";
import routes from "@/routes";
import { IMessage, ResponseRunThread } from "@/api/resources/OpenAI";
import Message from "./components/Message";
import LoaderDots from "./components/LoaderDots";
import useDetectScroll from "@smakss/react-scroll-direction";
import { getZodiacSignByDate } from "@/services/zodiac-sign";
import { useAuth } from "@/auth";

function AdvisorChatPage() {
  const isPrivateChat = window.location.href.includes("/advisor-chat-private/");
  const { id } = useParams();
  const api = useApi();
  const navigate = useNavigate();
  const { scrollDir, scrollPosition } = useDetectScroll();
  const token = useSelector(selectors.selectToken);
  const openAiToken = useSelector(selectors.selectOpenAiToken);
  const birthdate = useSelector(selectors.selectBirthdate);
  const zodiacSign = getZodiacSignByDate(birthdate);
  const { username } = useSelector(selectors.selectUser);
  const { gender, birthtime, birthPlace } = useSelector(
    selectors.selectQuestionnaire
  );
  const { user } = useAuth();
  const [assistant, setAssistant] = useState<IAssistant>();
  const [messageText, setMessageText] = useState("");
  const [textareaRows, setTextareaRows] = useState(1);
  const [messages, setMessages] = useState<IMessage[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [isLoadingSelfMessage, setIsLoadingSelfMessage] = useState(false);
  const [isLoadingAdvisorMessage, setIsLoadingAdvisorMessage] = useState(false);
  const [isLoadingLatestMessages, setIsLoadingLatestMessages] = useState(false);
  const timeOutStatusRunRef = useRef<NodeJS.Timeout>();
  const messagesEndRef = useRef<HTMLDivElement>(null);
  const [hasMoreLatestMessages, setHasMoreLatestMessages] = useState(true);

  useEffect(() => {
    if (
      scrollDir === "up" &&
      scrollPosition.top < 50 &&
      !isLoadingLatestMessages
    ) {
      loadLatestMessages();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [scrollDir, scrollPosition]);

  const loadLatestMessages = async () => {
    const lastIdMessage = messages[messages.length - 1]?.id;
    if (!hasMoreLatestMessages || !lastIdMessage) {
      return;
    }
    setIsLoadingLatestMessages(true);
    const listMessages = await api.getListMessages({
      token: openAiToken,
      method: "GET",
      path: routes.openAi.getListMessages(assistant?.external_chat_id || ""),
      QueryParams: {
        after: lastIdMessage,
        limit: 20,
      },
    });
    setHasMoreLatestMessages(listMessages.has_more);
    setMessages((prev) => [...prev, ...listMessages.data]);
    setIsLoadingLatestMessages(false);
  };

  const scrollToBottom = () => {
    setTimeout(() => {
      messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
    }, 100);
  };

  useEffect(() => {
    return () => {
      if (timeOutStatusRunRef.current) {
        clearTimeout(timeOutStatusRunRef.current);
      }
    };
  }, []);

  const getCurrentAssistant = (
    aiAssistants: Assistants.IAssistant[],
    idAssistant: string | number
  ) => {
    const currentAssistant = aiAssistants.find(
      (a) => a.external_id === idAssistant
    );
    return currentAssistant;
  };

  const updateMessages = async (threadId: string) => {
    const listMessages = await api.getListMessages({
      token: openAiToken,
      method: "GET",
      path: routes.openAi.getListMessages(threadId),
    });
    setMessages(listMessages.data);
    scrollToBottom();
  };

  const setExternalChatIdAssistant = async (threadId: string) => {
    await api.setExternalChatIdAssistant({
      token,
      chatId: String(assistant?.id || 1),
      ai_assistant_chat: {
        external_id: threadId,
      },
    });
  };

  const updateCurrentAssistant = async () => {
    const { ai_assistants } = await api.assistants({
      token,
    });
    const currentAssistant = getCurrentAssistant(ai_assistants, id || "");
    setAssistant(currentAssistant);
    return {
      ai_assistants,
      currentAssistant,
    };
  };

  const loadData = useCallback(async () => {
    const { ai_assistants, currentAssistant } = await updateCurrentAssistant();
    let listRuns: ResponseRunThread[] = [];
    if (currentAssistant?.external_chat_id?.length) {
      await updateMessages(currentAssistant.external_chat_id);
      const result = await api.getListRuns({
        token: openAiToken,
        method: "GET",
        path: routes.openAi.getListRuns(currentAssistant.external_chat_id),
      });
      listRuns = result.data;
    }

    setIsLoading(false);
    const runInProgress = listRuns.find((r) => r.status === "in_progress");

    setTimeout(() => {
      scrollToBottom();
    });

    if (runInProgress) {
      setIsLoadingAdvisorMessage(true);

      await checkStatusAndGetLastMessage(
        runInProgress.thread_id,
        runInProgress.id
      );
      setIsLoadingAdvisorMessage(false);
    }
    return { ai_assistants };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [api, id, openAiToken, token]);

  useApiCall<Assistants.Response>(loadData);

  const createThread = async (messageText: string) => {
    const thread = await api.createThread({
      token: openAiToken,
      method: "POST",
      path: routes.openAi.createThread(),
      messages: [
        {
          role: "user",
          content: messageText,
        },
      ],
    });
    await setExternalChatIdAssistant(thread.id);
    await updateCurrentAssistant();
    return thread;
  };

  const getStatusThreadRun = async (
    threadId: string,
    runId: string
  ): Promise<ResponseRunThread> => {
    await new Promise(
      (resolve) => (timeOutStatusRunRef.current = setTimeout(resolve, 1500))
    );
    const run = await api.getStatusRunThread({
      token: openAiToken,
      method: "GET",
      path: routes.openAi.getStatusRunThread(threadId, runId),
      assistant_id: `${assistant?.external_id}`,
    });
    if (run.status !== "completed") {
      return await getStatusThreadRun(threadId, runId);
    }
    return run;
  };

  const getContentMessage = (messageText: string) => {
    const sign = zodiacSign || user?.profile?.sign || "unknown";
    const _gender = gender || user?.profile?.gender || "unknown";
    const _birthDate = birthdate || user?.profile?.birthday || "unknown";
    const _birthtime = birthtime || "unknown";
    const _birthPlace = birthPlace || user?.profile?.birthplace || "unknown";
    const name = username || user?.profile?.full_name || "unknown";
    const content = `#USER INFO: zodiac sign - ${sign}; gender - ${_gender}; birthdate - ${_birthDate}; name - ${name}; birthtime - ${_birthtime}; birthPlace - ${_birthPlace}# ${messageText}`;
    return content;
  };

  const createMessage = async (messageText: string, threadId: string) => {
    const content = getContentMessage(messageText);
    const message = await api.createMessage({
      token: openAiToken,
      method: "POST",
      path: routes.openAi.createMessage(threadId),
      role: "user",
      content: content,
    });
    return message;
  };

  const runThread = async (threadId: string, assistantId: string) => {
    const run = await api.runThread({
      token: openAiToken,
      method: "POST",
      path: routes.openAi.runThread(threadId),
      assistant_id: assistantId,
    });
    return run;
  };

  const checkStatusAndGetLastMessage = async (
    threadId: string,
    runId: string
  ) => {
    const { status } = await getStatusThreadRun(threadId, runId);

    if (status === "completed") {
      await getLastMessage(threadId);
    }
  };

  const getLastMessage = async (threadId: string) => {
    const lastMessage = await api.getListMessages({
      token: openAiToken,
      method: "GET",
      path: routes.openAi.getListMessages(threadId),
      QueryParams: {
        limit: 1,
      },
    });
    setMessages((prev) => [lastMessage.data[0], ...prev]);
  };

  const sendMessage = async (messageText: string) => {
    setMessageText("");
    setIsLoadingSelfMessage(true);
    let threadId = "";
    let assistantId = "";

    if (assistant?.external_chat_id?.length) {
      const message = await createMessage(
        messageText,
        assistant.external_chat_id
      );
      setMessages((prev) => [message, ...prev]);
      setIsLoadingSelfMessage(false);
      setIsLoadingAdvisorMessage(true);
      threadId = assistant.external_chat_id;
      assistantId = assistant.external_id || "";
    } else {
      const content = getContentMessage(messageText);
      const thread = await createThread(content);
      threadId = thread.id;
      assistantId = assistant?.external_id || "";

      await getLastMessage(threadId);
      setIsLoadingSelfMessage(false);
      setIsLoadingAdvisorMessage(true);
    }
    setTimeout(() => {
      scrollToBottom();
    });

    const run = await runThread(threadId, assistantId);

    await checkStatusAndGetLastMessage(threadId, run.id);
    setTimeout(() => {
      scrollToBottom();
    });
    setIsLoadingAdvisorMessage(false);
  };

  const handleChangeMessageText = (e: ChangeEvent<HTMLTextAreaElement>) => {
    const { scrollHeight, clientHeight, value } = e.target;
    if (
      scrollHeight > clientHeight &&
      textareaRows < 5 &&
      value.length > messageText.length
    ) {
      setTextareaRows((prev) => prev + 1);
    }
    if (
      scrollHeight === clientHeight &&
      textareaRows > 1 &&
      value.length < messageText.length
    ) {
      setTextareaRows((prev) => prev - 1);
    }
    setMessageText(e.target.value);
  };

  const getIsSelfMessage = (role: string) => role === "user";

  const deleteDataFromMessage = (messageText: string) => {
    const splittedText = messageText.split("#");
    if (splittedText.length > 2) {
      return splittedText.slice(2).join("#");
    }
    return messageText;
  };

  useEffect(() => {
    (async () => {
      if (
        assistant &&
        !!assistant?.external_id &&
        !assistant?.external_chat_id?.length
      ) {
        await sendMessage("HI");
      }
    })();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [assistant?.external_id]);

  return (
    <section className={`${styles.page} page`}>
      {isLoading && (
        <Loader color={LoaderColor.Red} className={styles.loader} />
      )}
      {!isLoading && (
        <ChatHeader
          name={assistant?.name || ""}
          avatar={assistant?.photo?.th2x || ""}
          classNameContainer={styles["header-container"]}
          clickBackButton={() => navigate(-1)}
          hasBackButton={!isPrivateChat}
        />
      )}
      {!!messages.length && (
        <div className={styles["messages-container"]}>
          {isLoadingAdvisorMessage && (
            <Message
              avatar={assistant?.photo?.th2x || ""}
              text={<LoaderDots />}
              isSelf={false}
              backgroundTextColor={"#c9c9c9"}
              textColor={"#000"}
            />
          )}
          {messages.map((message) =>
            message.content.map((content) => (
              <Message
                avatar={assistant?.photo?.th2x || ""}
                text={deleteDataFromMessage(content.text.value)}
                isVisible={deleteDataFromMessage(content.text.value) !== " HI"}
                advisorName={assistant?.name || ""}
                backgroundTextColor={
                  getIsSelfMessage(message.role) ? "#0080ff" : "#c9c9c9"
                }
                textColor={getIsSelfMessage(message.role) ? "#fff" : "#000"}
                isSelf={getIsSelfMessage(message.role)}
                key={message.id}
              />
            ))
          )}
          <div className={styles["loader-container"]}>
            {isLoadingLatestMessages && <Loader color={LoaderColor.Red} />}
          </div>
        </div>
      )}

      <div ref={messagesEndRef} />
      {!isLoading && (
        <InputMessage
          placeholder="Text message"
          messageText={messageText}
          textareaRows={textareaRows}
          disabledTextArea={
            isLoadingAdvisorMessage || isLoadingSelfMessage || isLoading
          }
          disabledButton={!messageText.length}
          classNameContainer={styles["input-container"]}
          handleChangeMessageText={handleChangeMessageText}
          isLoading={isLoadingSelfMessage}
          submitForm={sendMessage}
        />
      )}
    </section>
  );
}

export default AdvisorChatPage;
