import React, { useRef, useEffect, useState } from "react";
import {
  FaTrash,
  FaChevronDown,
  FaChevronUp,
  FaPlusCircle,
} from "react-icons/fa";
import axios from "axios";
import "./index.css";
import baseurl from "../../api/url";
import Markdown from "react-markdown";
import remarkGfm from "remark-gfm";
import BounceLoader from "react-spinners/BounceLoader";

const Chat = () => {
  const bottomEl = useRef(null);
  const [messages, setMessages] = useState([]);
  const [query, setQuery] = useState("");
  const [file, setFile] = useState("");
  const [filename, setFilename] = useState("");
  const [uploadSuccess, setUploadSuccess] = useState(false);
  const [showLinkModal, setShowLinkModal] = useState(false);
  const [showDraftModal, setShowDraftModal] = useState(false);
  const [showReviewModal, setShowReviewModal] = useState(false);
  const [pdfLink, setPdfLink] = useState("");
  const [DraftEmail, setDraftEmail] = useState("");
  const [ReviewEmail, setReviewEmail] = useState("");
  const [currentView, setCurrentView] = useState("main");
  const [selectedTemplate, setSelectedTemplate] = useState(null);
  const [buttonsVisible, setButtonsVisible] = useState(true);
  const [subBubbleData, setSubBubbleData] = useState([]);
  const [mainBubbleData, setMainBubbleData] = useState([]);
  const [uploadLoading, setUploadLoading] = useState(false);
  const [deleteLoading, setDeleteLoading] = useState(false);
  const [content, setContent] = useState("");
  const [showPopup, setShowPopup] = useState(false);
  const [selectedOption, setSelectedOption] = useState("");
  const [uploadResponse, setUploadResponse] = useState(""); // State for streaming response

  // Scroll to the bottom when the component mounts or whenever content changes
  useEffect(() => {
    if (bottomEl.current) {
      bottomEl.current.scrollTop = bottomEl.current.scrollHeight;
    }
  }, [messages]);

  // Fetch Content and main-bubble data from the API
  useEffect(() => {
    const fetchContentAndMainBubbleData = async () => {
      try {
        // Fetch content first
        const user_email = JSON.parse(localStorage.getItem("user")).email;
        const page = "Chat-With-Content";
        const contentResponse = await axios.post(baseurl + "/fetch-content", {
          user_email,
          page,
        });

        if (contentResponse.status === 200) {
          setContent(contentResponse.data.text);

          // Fetch main bubble data
          const mainBubbleResponse = await axios.post(
            baseurl + "/main-bubble",
            { user_email, page }
          );

          if (mainBubbleResponse.status === 200) {
            setMainBubbleData(mainBubbleResponse.data.message);
          } else {
            console.error(
              "Error fetching main-bubble data:",
              mainBubbleResponse.data.message
            );
          }
        } else {
          console.error("Error fetching content:", contentResponse.data.text);
        }
      } catch (error) {
        console.error("Error fetching content and main-bubble data:", error);
      }
    };

    fetchContentAndMainBubbleData();
  }, []);

  // Toggle popup visibility
  const togglePopup = () => {
    if (content && content.length > 0) {
      setShowPopup(!showPopup);
    } else {
      alert(
        "Sorry you are currently not authorized to access this feature. In case of any queries, please send an email to <PRESIDIO_ANONYMIZED_EMAIL_ADDRESS>"
      );
    }
  };

  // Popup component or JSX block
  const PopupBox = ({ options, isOpen, onClose }) => {
    if (!isOpen) return null;

    const handleOptionChange = (event) => {
      setSelectedOption(event.target.value); // Update the selected option when it changes
    };

    const handleSubmit = () => {
      if (selectedOption) {
        if (selectedOption === "Chat-With-Link") {
          setShowLinkModal(true);
        }
        if (selectedOption === "Chat-With-PDF") {
          const fileInput = document.createElement("input");
          fileInput.type = "file";
          fileInput.accept = ".pdf"; // Set accepted file types
          fileInput.onchange = (e) => handleFileChange(e);
          fileInput.click(); // Trigger the file input click event to open the file selection dialog
        }

        if (selectedOption === "Draft-Email") {
          setShowDraftModal(true);
        }

        if (selectedOption === "Review-Email") {
          setShowReviewModal(true);
        }
      }
      onClose();
    };

    return (
      <div className="fixed inset-0 flex items-center justify-center bg-gray-800 bg-opacity-50 z-50">
        <div className="bg-white p-8 rounded-lg shadow-lg w-1/3">
          <h2 className="text-lg font-semibold mb-4">Select an Option</h2>
          <select
            className="w-full border border-gray-300 p-2 rounded-lg mb-4"
            value={selectedOption}
            onChange={handleOptionChange}
          >
            <option value="">Select an option</option>
            {options.map((option, index) => (
              <option key={index} value={option}>
                {option}
              </option>
            ))}
          </select>
          <div className="flex justify-center">
            <div className="mx-2">
              <button
                className="bg-blue-500 text-white px-4 py-2 rounded-lg w-max"
                onClick={handleSubmit} // Call handleSubmit when the "Submit" button is clicked
              >
                Submit
              </button>
            </div>
            <div className="mx-2">
              <button
                className="bg-blue-500 text-white px-4 py-2 rounded-lg w-max"
                onClick={onClose}
              >
                Close
              </button>
            </div>
          </div>
        </div>
      </div>
    );
  };

  // Send Review-Email request to the server
  const sendReviewRequest = async (query) => {
    let index = -1;
    setMessages((prevMessages) => {
      index = prevMessages.length;
      return [...prevMessages, { message: query, prompt: query }];
    });

    const context_history = messages
      .flatMap((message) => [
        { role: "user", content: message.message },
        message.api_output && {
          role: "assistant",
          content: message.api_output.text,
        },
      ])
      .filter(Boolean);
    try {
      const res = await fetch(baseurl + "/review-email", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          prompt: query,
          page: "Chat-With-Content",
          user_email: JSON.parse(localStorage.getItem("user")).email,
        }),
      });

      const reader = res.body.getReader();
      const decoder = new TextDecoder("utf-8");
      while (true) {
        const { value, done } = await reader.read();
        if (done) break;
        const chunk = decoder.decode(value, { stream: true });
        setMessages((prevMessages) => {
          const updatedMessages = [...prevMessages];
          if (!updatedMessages[index].api_output) {
            updatedMessages[index].api_output = { text: "" };
          }
          updatedMessages[index].api_output.text += chunk;
          return updatedMessages;
        });
      }
    } catch (error) {
      console.error("Error in getting response:", error);
    }
  };

  // Send Draft-Email request to the server
  const sendDraftRequest = async (query) => {
    let index = -1;
    setMessages((prevMessages) => {
      index = prevMessages.length;
      return [...prevMessages, { message: query, prompt: query }];
    });

    const context_history = messages
      .flatMap((message) => [
        { role: "user", content: message.message },
        message.api_output && {
          role: "assistant",
          content: message.api_output.text,
        },
      ])
      .filter(Boolean);
    try {
      const res = await fetch(baseurl + "/draft-email", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          prompt: query,
          page: "Chat-With-Content",
          user_email: JSON.parse(localStorage.getItem("user")).email,
        }),
      });

      const reader = res.body.getReader();
      const decoder = new TextDecoder("utf-8");
      while (true) {
        const { value, done } = await reader.read();
        if (done) break;
        const chunk = decoder.decode(value, { stream: true });
        setMessages((prevMessages) => {
          const updatedMessages = [...prevMessages];
          if (!updatedMessages[index].api_output) {
            updatedMessages[index].api_output = { text: "" };
          }
          updatedMessages[index].api_output.text += chunk;
          return updatedMessages;
        });
      }
    } catch (error) {
      console.error("Error in getting response:", error);
    }
  };

  // Send chat request to the server
  const sendChatRequest = async (mail) => {
    let index = -1;
    setMessages((prevMessages) => {
      index = prevMessages.length;
      return [...prevMessages, { message: mail, prompt: query }];
    });

    const context_history = messages
      .flatMap((message) => [
        { role: "user", content: message.message },
        message.api_output && {
          role: "assistant",
          content: message.api_output.text,
        },
      ])
      .filter(Boolean);

    try {
      setQuery("");
      const res = await fetch(baseurl + "/answer", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          prompt: mail,
          doc: filename,
          history: context_history,
          user_email: JSON.parse(localStorage.getItem("user")).email,
        }),
      });

      const reader = res.body.getReader();
      const decoder = new TextDecoder("utf-8");
      while (true) {
        const { value, done } = await reader.read();
        if (done) break;
        const chunk = decoder.decode(value, { stream: true });
        setMessages((prevMessages) => {
          const updatedMessages = [...prevMessages];
          if (!updatedMessages[index].api_output) {
            updatedMessages[index].api_output = { text: "" };
          }
          updatedMessages[index].api_output.text += chunk;
          return updatedMessages;
        });
      }
    } catch (error) {
      console.error("Error in getting response:", error);
    }
  };

  // Handle user query change
  const handleUserQueryChange = (e) => {
    setQuery(e.target.value);
  };

  // When File Submitted Call the API to process further.
  const handleFileChange = async (event) => {
    const selectedFile = event.target.files[0];
    if (selectedFile && selectedFile.type === "application/pdf") {
      setFile(selectedFile.name);
      setUploadLoading(true);
      setUploadSuccess(false);
      setUploadResponse(""); // Reset the response each time a new file is uploaded

      const formData = new FormData();
      formData.append("file", selectedFile);

      try {
        const response = await fetch(baseurl + "/upload-doc", {
          method: "POST",
          body: formData,
        });

        // Check if the response is OK
        if (!response.ok) {
          alert("An error occured please try after some time...");
          throw new Error("Network response was not ok" + response.text);
        }
        const reader = response.body.getReader(); // Create a reader from the response
        const decoder = new TextDecoder("utf-8");
        let result = "";
        while (true) {
          const { value, done } = await reader.read();
          if (done) break; // Break the loop if done
          const chunk = decoder.decode(value, { stream: true });
          result += chunk; // Append new chunk to the result
          setUploadResponse(chunk);
        }

        // Regular expression to find the last message without needing to trim
        const messagePattern = /Message\s*:\s*(\/[^ ]+|False)\s*$/gm;

        // Extracting the value directly without using 'Message:'
        const matches1 = result.match(messagePattern);
        const vdb =
          matches1 && matches1.length > 0
            ? matches1[matches1.length - 1].replace(/.*:\s*/, "")
            : "";
        console.log(vdb);
        if (vdb === "False") {
          throw new Error("Failed to Upload the file.");
        }

        // Finalizing response
        setUploadSuccess(true);
        setFilename(selectedFile.name);
        setFilename(vdb);
      } catch (error) {
        console.error("Error uploading file:", error);
        setUploadSuccess(false);
        alert("File upload failed. Please try again after some time.");
      } finally {
        setUploadLoading(false);
      }
    } else if (selectedFile) {
      alert("Please upload a PDF file.");
    }
  };

  // Handle document upload via link
  const handleLinkUpload = async () => {
    alert(
      "This feature is currently not available. Please use the document upload functionality instead"
    );
    setShowLinkModal(false);
    setShowPopup(true);
  };

  // Handle Draft Email
  const handleDraftEmail = async () => {
    console.log(DraftEmail);
    setShowDraftModal(false);
    setShowPopup(false);
    sendDraftRequest(DraftEmail);
  };

  // Handle Review Email
  const handleReviewEmail = async () => {
    console.log(ReviewEmail);
    setShowReviewModal(false);
    setShowPopup(false);
    sendReviewRequest(ReviewEmail);
  };

  // Handle the deletion of the uploaded document
  const handleDeleteFile = async (folderName) => {
    try {
      setDeleteLoading(true);
      const response = await fetch(baseurl + "/delete-embeddings", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ folder_name: folderName }),
      });

      if (!response.ok) {
        const errorMessage = await response.json();
        console.error("Error while deleting File/Embedding:", errorMessage);
      } else {
        setUploadSuccess(false);
      }
    } catch (error) {
      console.error("Error while deleting File/Embedding:", error);
      window.location.reload(); // reload the site
    } finally {
      setDeleteLoading(false);
    }
  };

  // Handle main bubble click
  const handleMainDivClick = async (item) => {
    setSelectedTemplate(item);
    setCurrentView("sub");

    const query = Object.values(item)[0];
    sendChatRequest(query);

    try {
      const user_email = JSON.parse(localStorage.getItem("user")).email;
      const page = "Chat-With-Content";
      const main_bubble_name = Object.keys(item)[0];

      const response = await axios.post(baseurl + "/sub-bubble", {
        user_email,
        page,
        main_bubble_name,
      });

      if (response.status === 200) {
        setSubBubbleData(response.data.message);
      } else {
        console.error("Error fetching sub-bubble data:", response.data.message);
      }
    } catch (error) {
      console.error("Error fetching sub-bubble data:", error);
    }
  };

  // Handle sub bubble click
  const handleSubDivClick = (subItem) => {
    const value = Object.values(subItem)[0];
    setQuery(value);
    sendChatRequest(value);
  };

  // Handle back button click
  const handleBackClick = () => {
    setCurrentView("main");
  };

  // Toggle the visibility of the buttons
  const toggleButtonsVisibility = () => {
    setButtonsVisible(!buttonsVisible);
  };

  // Determine the placeholder text for the query text box
  const getTextPlaceholder = () => {
    if (uploadLoading) {
      return uploadResponse || "Processing...";
    }
    if (deleteLoading) {
      return "Deleting file, please wait...";
    }
    if (!uploadSuccess) {
      return "Select the Content to get started...";
    }
    return "Document Uploaded Successfully...";
  };

  // Determine the placeholder text for the query text box
  const getQueryPlaceholder = () => {
    if (uploadLoading) {
      return "Uploading file, please wait...";
    }
    if (deleteLoading) {
      return "Deleting file, please wait...";
    }
    if (!uploadSuccess) {
      return "Select the Content to get started...";
    }
    return "Enter your query to start chat...";
  };

  return (
    <div className="flex flex-col">
      {/* Display the filename at the top if upload is successful */}
      {uploadSuccess && (
        <div className="bg-gray-200 p-4">
          <p className="text-lg font-semibold">Uploaded Document: {file}</p>
        </div>
      )}
      <div ref={bottomEl} className="min-h-[70vh] flex-grow overflow-y-auto">
        {messages.length !== 0 ? (
          messages.map((item, index) => (
            <div className="flex flex-col space-y-4 p-6" key={index}>
              <div className="max-w-75p flex items-center self-end rounded-xl rounded-tr bg-blue-500 py-4 px-6 text-white">
                <p className="text-lg" style={{ whiteSpace: "pre-line" }}>
                  {item.message}
                </p>
              </div>
              {item.api_output !== undefined ? (
                <div className="group relative flex items-center">
                  {item.api_output?.text && (
                    <div className="flex items-center self-start rounded-xl rounded-tl bg-white py-4 px-6">
                      <Markdown className="prose" remarkPlugins={[remarkGfm]}>
                        {item.api_output?.text}
                      </Markdown>
                    </div>
                  )}
                </div>
              ) : (
                <div className="group relative flex items-center">
                  <div className="flex items-center self-start rounded-xl rounded-tl bg-white py-4 px-6">
                    <div className="typing">
                      <div className="typing__dot"></div>
                      <div className="typing__dot"></div>
                      <div className="typing__dot"></div>
                    </div>
                  </div>
                </div>
              )}
            </div>
          ))
        ) : (
          <div className="default-text max-h-[60vh] text-2xl flex justify-center items-center">
            {uploadLoading
              ? uploadResponse || "Processing..."
              : !uploadSuccess
              ? "Select the Content to get started..."
              : "Document Uploaded Successfully..."}
          </div>
        )}
      </div>
      {uploadLoading && (
        <div className="loader-container">
          <BounceLoader
            color={"Blue"}
            loading={uploadLoading || deleteLoading}
            size={60}
          />
        </div>
      )}
      {uploadSuccess && (
        <div className="main-div flex flex-wrap justify-center items-center space-x-4 p-4">
          {buttonsVisible && (
            <>
              {currentView === "main" ? (
                mainBubbleData.length > 0 &&
                mainBubbleData.map((item, index) => (
                  <button
                    key={index}
                    className="sub-div bg-blue-500 text-white px-4 py-2 rounded-lg shadow-md hover:bg-blue-600 m-2"
                    onClick={() => handleMainDivClick(item)}
                  >
                    {Object.keys(item)[0]}
                  </button>
                ))
              ) : (
                <>
                  {selectedTemplate &&
                    Object.keys(selectedTemplate).length > 0 && (
                      <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
                        {subBubbleData.map((subItem, index) => (
                          <button
                            key={index}
                            className="sub-div bg-blue-500 text-white px-4 py-2 rounded-lg shadow-md hover:bg-blue-600"
                            onClick={() => handleSubDivClick(subItem)}
                          >
                            {Object.keys(subItem)[0]}
                          </button>
                        ))}
                      </div>
                    )}
                  <button
                    className="sub-div bg-red-500 text-white px-4 py-2 rounded-lg shadow-md hover:bg-red-600 m-2"
                    onClick={handleBackClick}
                  >
                    Back
                  </button>
                </>
              )}
            </>
          )}
          {(mainBubbleData.length > 0 || subBubbleData.length > 0) && (
            <button
              className="ml-4 rounded-lg bg-gray-500 p-2 text-white"
              onClick={toggleButtonsVisibility}
              title="Toggle buttons visibility"
            >
              {buttonsVisible ? (
                <FaChevronDown className="h-6 w-6" />
              ) : (
                <FaChevronUp className="h-6 w-6" />
              )}
            </button>
          )}
        </div>
      )}
      <div className="flex items-center justify-between p-6">
        {/* Query text box */}
        <div className="flex-grow mr-2">
          <textarea
            id="body"
            name="body"
            className="w-full rounded-lg border border-gray-300 px-4 py-3 text-lg"
            placeholder={getQueryPlaceholder()}
            rows="2"
            value={query}
            onChange={handleUserQueryChange}
            onKeyDown={(e) => {
              if (e.key === "Enter" && !e.shiftKey) {
                e.preventDefault();
                if (query.trim()) {
                  sendChatRequest(query);
                } else {
                  alert("Please enter a query");
                }
              }
            }}
            onPaste={(e) => e.preventDefault()}
            disabled={uploadLoading || !uploadSuccess}
          ></textarea>
        </div>
        <div className="flex items-center">
          {/* Conditionally render upload or delete button */}
          {!uploadSuccess ? (
            <>
              <div className="ml-4">
                <button
                  className="ml-4 text-blue-500"
                  onClick={togglePopup}
                  title="Open Popup"
                >
                  <FaPlusCircle className="h-6 w-6" />
                </button>
              </div>
              {/* Popup box */}
              <PopupBox
                options={content} // Use content array directly
                isOpen={showPopup}
                onClose={() => setShowPopup(false)}
              />
            </>
          ) : (
            <button
              className="ml-4 rounded-lg bg-red-500 p-2 text-white"
              onClick={() => handleDeleteFile(filename)}
              title="Delete uploaded document"
            >
              <FaTrash className="h-6 w-6" />
            </button>
          )}
        </div>
      </div>
      {showLinkModal && (
        <div className="fixed inset-0 flex items-center justify-center bg-gray-800 bg-opacity-50">
          <div className="bg-white p-8 rounded-lg shadow-lg w-2/3">
            <h2 className="text-lg font-semibold mb-4">Enter PDF Link</h2>
            <input
              type="text"
              className="w-full border border-gray-300 p-2 rounded-lg mb-4"
              placeholder="Enter PDF link here..."
              value={pdfLink}
              onChange={(e) => setPdfLink(e.target.value)}
            />
            <div className="flex justify-end">
              <button
                className="mr-2 bg-gray-300 px-4 py-2 rounded-lg"
                onClick={() => setShowLinkModal(false)}
              >
                Cancel
              </button>
              <button
                className="bg-blue-500 text-white px-4 py-2 rounded-lg"
                onClick={handleLinkUpload}
              >
                Upload
              </button>
            </div>
          </div>
        </div>
      )}
      {showDraftModal && (
        <div className="fixed inset-0 flex items-center justify-center bg-gray-800 bg-opacity-50">
          <div className="bg-white p-8 rounded-lg shadow-lg w-2/3 h-2/3">
            <h2 className="text-lg font-semibold mb-4">
              Enter Subject to Draft the mail.
            </h2>
            <textarea
              rows={15} // Specifies the number of visible text lines
              cols={120} // Specifies the width of the textarea in characters
              placeholder="Enter Instructions to Draft the Email..." // Specifies a short hint that describes the expected value of the textarea
              name="name" // Specifies the name of the textarea, which can be used when submitting a form
              autoFocus={true}
              onChange={(e) => setDraftEmail(e.target.value)}
            />
            <div className="flex justify-end">
              <button
                className="mr-2 bg-gray-300 px-4 py-2 rounded-lg"
                onClick={() => setShowDraftModal(false)}
              >
                Cancel
              </button>
              <button
                className="bg-blue-500 text-white px-4 py-2 rounded-lg"
                onClick={handleDraftEmail}
              >
                Submit
              </button>
            </div>
          </div>
        </div>
      )}
      {showReviewModal && (
        <div className="fixed inset-0 flex items-center justify-center bg-gray-800 bg-opacity-50">
          <div className="bg-white p-8 rounded-lg shadow-lg w-2/3">
            <h2 className="text-lg font-semibold mb-4">
              Enter Email to Review
            </h2>
            <textarea
              rows={15} // Specifies the number of visible text lines
              cols={120} // Specifies the width of the textarea in characters
              placeholder="Enter Email for Review..." // Specifies a short hint that describes the expected value of the textarea
              name="name" // Specifies the name of the textarea, which can be used when submitting a form
              autoFocus={true}
              onChange={(e) => setReviewEmail(e.target.value)}
            />
            <div className="flex justify-end">
              <button
                className="mr-2 bg-gray-300 px-4 py-2 rounded-lg"
                onClick={() => setShowReviewModal(false)}
              >
                Cancel
              </button>
              <button
                className="bg-blue-500 text-white px-4 py-2 rounded-lg"
                onClick={handleReviewEmail}
              >
                Submit
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

export default Chat;



This is UI Code.



@app.route("/upload-link", methods=['POST'])
def upload_link():
    try:
        start = time.time()
        data = request.get_json()
        pdf_url = data.get('pdf_url')
        logger.info(f"/upload-link - PDF_URL: {pdf_url}")

        if not pdf_url:
            logger.error(f"Error - No PDF URL Provided : {time.time()-start}")
            return jsonify({"text": "No PDF URL provided"}), 400
        
        # Create a streaming response
        response =  Response(create_VectorDB_link(pdf_url), content_type='text/event-stream')
        response.headers["X-Accel-Buffering"] = "no"
        response.headers['Cache-Control'] = 'no-cache'  # Prevent caching
        return response
        
    # General Exception
    except Exception as e:
        logger.error(f"Error in /upload-link: {e}.")
        return jsonify({'text': f"Error: {e}"}), 500


I want to have upload via link, when we click on Chat-With-Link we have similar functionality like Chat-with-PDF . instead of upload we show modal and tell user to enter pdf link and we call the backend api similar to we did in handlefilechange