import {
  Box,
  Button,
  InputAdornment,
  TextField,
  Typography,
} from "@mui/material";
import React, { useRef, useContext, useEffect, useState } from "react";
import {
  Conversation,
  ConversationPrompt,
  ConversationPromptType,
  ConversationType,
} from "../../data";
import { axiosInstance } from "../../../axiosApi";
import { AxiosResponse } from "axios";
import ConversationDetailCard from "../ConversationDetailCard/ConversationDetailCard";
import { ErrorMessage, Field, Form, Formik } from "formik";
import { WebSocketContext } from "../../contexts/WebSocketContext";
import { AccountContext } from "../../contexts/AccountContext";
import * as Yup from "yup";
import { styled } from "@mui/material/styles";
import { AttachFile, Send } from "@mui/icons-material";
import Markdown from "markdown-to-jsx";

type RenderMessageProps = {
  response: string;
  index: number;
};

type AsyncChatProps = {
  selectedConversation: number | undefined;
  conversationTitle: string;
  conversations: Conversation[];
  setConversations: React.Dispatch<React.SetStateAction<Conversation[]>>;
  setSelectedConversation: React.Dispatch<
    React.SetStateAction<number | undefined>
  >;
  drawerWidth: number;
};

const validationSchema = Yup.object().shape({
  prompt: Yup.string()
    .trim()
    .min(1, "Need to have at least one character")
    .required("This is requried"),
});

const VisuallyHiddenInput = styled("input")({
  clip: "rect(0 0 0 0)",
  clipPath: "inset(50%)",
  height: 1,
  overflow: "hidden",
  position: "absolute",
  bottom: 0,
  left: 0,
  whiteSpace: "nowrap",
  width: 1,
});

const AsyncChat = ({
  selectedConversation,
  conversationTitle,
  conversations,
  setConversations,
  setSelectedConversation,
  drawerWidth,
}: AsyncChatProps): JSX.Element => {
  const messageEndRef = useRef(null);

  const [conversationDetails, setConversationDetails] = useState<
    ConversationPrompt[]
  >([]);
  const [disableInput, setDisableInput] = useState<boolean>(false);

  const [subscribe, unsubscribe, socket, sendMessage] =
    useContext(WebSocketContext);
  const { account, setAccount } = useContext(AccountContext);
  const messageRef = useRef("");
  const messageResponsePart = useRef(0);
  const conversationRef = useRef(conversationDetails);
  const [stateMessage, setStateMessage] = useState<string>("");
  const selectedConversationRef = useRef<undefined | number>(undefined);

  useEffect(() => {
    /* register a consistent channel name for identifing this chat messages */
    const channelName = `ACCOUNT_ID_${account?.email}`;

    /* subscribe to channel and register callback */
    subscribe(channelName, (message: string) => {
      /* when a message is received just add it to the UI */

      if (message === "END_OF_THE_STREAM_ENDER_GAME_42") {
        messageResponsePart.current = 0;

        conversationRef.current.pop();

        //handleAssistantPrompt({prompt: messageRef.current})
        setConversationDetails([
          ...conversationRef.current,
          new ConversationPrompt({
            message: `${messageRef.current}`,
            user_created: false,
          }),
        ]);
        messageRef.current = "";
        setStateMessage("");
      } else if (message === "START_OF_THE_STREAM_ENDER_GAME_42") {
        messageResponsePart.current = 2;
      } else if (message === "CONVERSATION_ID") {
        messageResponsePart.current = 1;
      } else {
        if (messageResponsePart.current === 1) {
          // this has to do with the conversation id
          if (!selectedConversation) {
            //console.log("we have a new conversation")
            setSelectedConversation(Number(message));
          }
        } else if (messageResponsePart.current === 2) {
          let contentToAdd = message;
          try {
            const parsedMessage = JSON.parse(message);
            if (
              parsedMessage &&
              typeof parsedMessage === "object" &&
              parsedMessage.type
            ) {
              switch (parsedMessage.type) {
                case "text":
                  contentToAdd = parsedMessage.content;
                  break;
                case "plot":
                  contentToAdd = `<plot format="${parsedMessage.format}" image="${parsedMessage.image}"></plot>`;
                  break;
                case "error":
                  contentToAdd = `<error content="${parsedMessage.content}"></error>`;
                  break;
                default:
                  // contentToAdd is already `message`
                  break;
              }
            }
          } catch (e) {
            // Not a JSON object, treat as raw string.
            // contentToAdd is already `message`.
          }

          messageRef.current += contentToAdd;
          setStateMessage(messageRef.current);
        }
      }
    });

    return () => {
      /* unsubscribe from channel during cleanup */
      unsubscribe(channelName);
    };
  }, [account, subscribe, unsubscribe]);

  async function GetConversationDetails() {
    if (selectedConversation) {
      try {
        //console.log('GetConversationDetails')
        //setPromptProcessing(true)
        selectedConversationRef.current = selectedConversation;
        const { data }: AxiosResponse<ConversationPromptType[]> =
          await axiosInstance.get(
            `conversation_details?conversation_id=${selectedConversation}`,
          );

        const tempConversations: ConversationPrompt[] = data.map(
          (item) =>
            new ConversationPrompt({
              message: item.message,
              user_created: item.user_created,
              created_timestamp: item.created_timestamp,
            }),
        );
        conversationRef.current = tempConversations;
        setConversationDetails(tempConversations);
      } finally {
        //setPromptProcessing(false)
      }
    }
  }

  useEffect(() => {
    GetConversationDetails();
  }, [selectedConversation]);

  // Function to render each message

  const renderMessage = ({ response, index }: RenderMessageProps) => (
    <div key={index}>
      <p>{response}</p>
    </div>
  );

  type PromptValues = {
    prompt: string;
    file: Blob | null;
    fileType: string | null;
  };

  const handlePromptSubmit = async (
    { prompt, file, fileType }: PromptValues,
    { resetForm }: any,
  ): Promise<void> => {
    // send the prompt to be saved
    const trimmedPrompt = (prompt ?? "").trim();
    if (!trimmedPrompt) {
      return;
    }
    console.log(fileType);
    try {
      const tempConversations: ConversationPrompt[] = [
        ...conversationDetails,
        new ConversationPrompt({ message: trimmedPrompt, user_created: true }),
        new ConversationPrompt({ message: "", user_created: false }),
      ];
      conversationRef.current = tempConversations;
      setConversationDetails(tempConversations);
      // TODO: add the file here
      console.log(
        `Sending message. ${trimmedPrompt} ${selectedConversation} ${fileType}`,
      );
      sendMessage(trimmedPrompt, selectedConversation, file, fileType);
      resetForm();
    } catch (e) {
      console.log(`error ${e}`);
      // TODO: make this user friendly
    }
  };

  return (
    <Box
      sx={{
        flexGrow: 1,
        p: 3,
        width: { sm: `calc(100% - ${drawerWidth}px)` },
        ml: { sm: `${drawerWidth}px` },
        mt: 5,
      }}
      position="fixed"
    >
      <div className="card" style={{ height: "auto" }}>
        <div className="card-header">
          <div className="bg-gradient-dark shadow-dark border-radius-lg py-3 pe-1">
            <Typography
              variant="h6"
              style={{ flexGrow: 1, marginLeft: "1rem" }}
              className="text-white font-weight-bold"
            >
              {conversationTitle}
            </Typography>
          </div>
        </div>
        <div className="card-body bg-gradient-dark border-radius-lg py-3 pe-1">
          <Box
            sx={{
              mb: 2,
              display: "flex",
              flexDirection: "column",
              height: 700,
              overflow: "hidden",
              overflowY: "scroll",
              //justifyContent="flex-end" //# DO NOT USE THIS WITH 'scroll'
            }}
          >
            {selectedConversation ? (
              conversationDetails.map((convo_detail) =>
                convo_detail.message.length > 0 ? (
                  <ConversationDetailCard
                    message={convo_detail.message}
                    user_created={convo_detail.user_created}
                    key={convo_detail.id}
                  />
                ) : (
                  <ConversationDetailCard
                    message={stateMessage}
                    user_created={convo_detail.user_created}
                    key={convo_detail.id}
                  />
                ),
              )
            ) : (
              <Markdown className="text-light text-center">
                Either select a previous conversation on start a new one.
              </Markdown>
            )}
            <div ref={messageEndRef} />
          </Box>
        </div>
      </div>

      <div className="card-footer">
        <Formik
          initialValues={{
            prompt: "",
            file: null,
            fileType: null,
          }}
          onSubmit={handlePromptSubmit}
          validationSchema={validationSchema}
        >
          {(formik) => (
            <Form>
              <div
                className="row"
                style={{
                  position: "sticky",
                  bottom: 0,
                }}
              >
                <div className="col-12">
                  <Field
                    name={"prompt"}
                    fullWidth
                    as={TextField}
                    label={"Prompt"}
                    errorstring={<ErrorMessage name={"prompt"} />}
                    size={"small"}
                    role={undefined}
                    tabIndex={-1}
                    margin={"dense"}
                    variant={"outlined"}
                    InputProps={{
                      endAdornment: (
                        <InputAdornment position="end">
                          <Button
                            component="label"
                            color={formik.values.file ? "success" : "inherit"}
                            startIcon={<AttachFile />}
                            role={undefined}
                            tabIndex={-1}
                          >
                            <VisuallyHiddenInput
                              type="file"
                              accept=".csv,.xlsx,.txt"
                              onChange={(event) => {
                                const file = event.target.files?.[0];
                                //console.log(file)
                                if (file) {
                                  formik.setFieldValue("file", file);
                                  formik.setFieldValue("fileType", file.type);
                                } else {
                                  formik.setFieldValue("file", null);
                                  formik.setFieldValue("fileType", null);
                                }
                              }}
                            />
                          </Button>
                          <Button
                            startIcon={<Send />}
                            type={"submit"}
                            disabled={!formik.isValid}
                          ></Button>
                        </InputAdornment>
                      ),
                    }}
                  ></Field>
                </div>
                {/* <div className='col-1'>
                                    <Button
                                    type={'submit'}
                                    // disabled={selectedConversation === undefined ? true : false || !formik.isValid}
                                    >Send

                                    </Button>
                                </div> */}
              </div>
            </Form>
          )}
        </Formik>
      </div>
    </Box>
  );
};

export default AsyncChat;
