import { generateSessionId } from "../components/api";
import { SendMessageType } from "../components/types";
import { eventToMessageMapping } from "../lib/constant";
import { addMessage, getBaseurl, updateLastMessage } from "../lib/utils";

export const useMessage = (apiKey: string) => {
  const sendMessage = async ({
    environment,
    modelEndpointId,
    input,
    setMessages,
    sessionId,
    setInput,
    setIsInputDisabled,
    setSessionId,
    moreAddedAgents,
    fulfillmentPrompt,
    contextVariables,
    botId,
    reasoningMode,
  }: SendMessageType) => {
    if (!input) return;

    addMessage(setMessages, {
      message: input,
      author: "user",
      isFetching: false,
      id: Date.now().toString(),
      status: "complete",
    });

    addMessage(setMessages, {
      author: "bot",
      isFetching: true,
      status: "loading",
    });

    setIsInputDisabled(true);
    setInput("");

    try {
      let currentSessionId = sessionId;
      if (!currentSessionId) {
        const sessionId = await generateSessionId(
          apiKey,
          environment,
          botId,
          contextVariables
        );
        currentSessionId = sessionId;
        setSessionId(currentSessionId);
      }

      const queryData = {
        endpointId: modelEndpointId,
        query: input,
        pluginIds: moreAddedAgents,
        responseMode: "stream",
        debugMode: "on",
        reasoningMode: reasoningMode,
        modelConfigs: {
          frequencyPenalty: 0,
          fulfillmentPrompt: fulfillmentPrompt,
          presencePenalty: 0,
          stopTokens: [],
          temperature: 0.3,
          topP: 0.9,
        },
      };

      const url = getBaseurl(environment);
      const response = await fetch(
        `${url}/chat/v1/sessions/${currentSessionId}/query`,
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "x-bot-id": botId,
            apikey: apiKey!,
          },
          body: JSON.stringify(queryData),
        }
      );

      if (!response.ok) {
        throw new Error(`Request failed with status ${response.status}`);
      }

      const reader = response.body?.getReader();
      if (!reader) {
        throw new Error("Response stream is not available.");
      }

      const decoder = new TextDecoder("utf-8");
      let sseBuffer = "";
      let isStreamComplete = false;

      // eslint-disable-next-line no-constant-condition
      while (!isStreamComplete) {
        const { done, value } = await reader.read();
        if (done) break;

        sseBuffer += decoder.decode(value, { stream: true });
        const events = sseBuffer.split("\n\n");
        sseBuffer = events.pop() ?? "";

        for (const rawEvent of events) {
          const eventLines = rawEvent
            .split("\n")
            .map((line) => line.trim())
            .filter(Boolean);

          if (!eventLines.length) continue;

          const eventName =
            eventLines
              .find((line) => line.startsWith("event:"))
              ?.replace("event:", "")
              .trim() ?? "message";
          const dataPayload = eventLines
            .filter((line) => line.startsWith("data:"))
            .map((line) => line.replace("data:", "").trim())
            .join("\n");

          if (!dataPayload) continue;
          if (dataPayload === "[DONE]") {
            isStreamComplete = true;
            break;
          }

          let line: any;
          try {
            line = JSON.parse(dataPayload);
          } catch {
            continue;
          }

          if (eventName === "heartbeat") continue;

          if (
            line.eventType === "statusLog" &&
            line.currentStatusLog.statusMessage !== "Fulfillment completed"
          ) {
            updateLastMessage(setMessages, {
              message:
                eventToMessageMapping[line.currentStatusLog.statusMessage],
              isFetching: false,
              status: "loading",
            });
          } else if (line.eventType === "fulfillment") {
            updateLastMessage(setMessages, (prev) => ({
              message:
                prev?.status === "loading"
                  ? line.answer
                  : prev.message + line.answer,
              status: "streaming",
              isFetching: false,
            }));
          } else if (
            line.eventType === "statusLog" &&
            line.currentStatusLog.statusMessage === "Fulfillment completed"
          ) {
            updateLastMessage(setMessages, (prev) => ({
              message: prev?.message ?? line.currentStatusLog.answer ?? "",
              status: "complete",
              isFetching: false,
            }));
          }
        }
      }
    } catch (error) {
      console.error("Error sending message:", error);
      updateLastMessage(setMessages, {
        message: "An error occurred while processing your request.",
        status: "error",
        isFetching: false,
      });
    } finally {
      setIsInputDisabled(false);
    }
  };

  return { sendMessage };
};
