import streamlit as st
import requests
import re
import streamlit_authenticator as stauth
import os
import yaml
from yaml.loader import SafeLoader
from pathlib import Path
from drive_search import search_file_in_drive
from datetime import datetime
from feedback_manager import FeedbackManager


class ChatbotApp:
    def __init__(self):
        # Configura título, ícone e layout da página
        st.set_page_config(page_title="Sicoob Chatbot 🤖", page_icon="logos/sicoob-ico.ico", layout="wide")
        self.backend_url = "http://localhost:5001/chat"
        self.title = "Sicoob Chatbot"
        self.description = "Este assistente virtual pode te ajudar com informações sobre documentos da Sicoob."
        self.caption = "Confira as atualizações no botão 'Atualizações'."
        self.style_dir = Path("./logos/styles")
        self.load_styles()
        self.feedback_manager = FeedbackManager()
        st.session_state.first = False
        if "theme" not in st.session_state:
            st.session_state.theme = "light"
        self.configyml = os.path.join(os.getcwd(), "./files/config.yaml")
        if "feedback_submitted" not in st.session_state:
            st.session_state.feedback_submitted = set()

    def load_styles(self):
        try:
            self.base_style = (self.style_dir / "base.css").read_text()
            self.light_style = (self.style_dir / "light.css").read_text()
            self.dark_style = (self.style_dir / "dark.css").read_text()
        except FileNotFoundError as e:
            st.error(f"Error loading styles: {e}")
            self.base_style = ""
            self.light_style = ""
            self.dark_style = ""

    def change_style(self):
        with st.sidebar:
            if st.session_state.theme == "light":
                st.logo("./logos/sicoob-logo-horizontal-light.png", icon_image="./logos/sicoob-logo-vertical-sm.png")
            else:
                st.logo("./logos/sicoob-logo-horizontal-dark.png", icon_image="./logos/sicoob-logo-vertical-sm.png")
        st.session_state.theme = "dark" if st.session_state.theme == "light" else "light"

    @st.dialog("📄 Atualizações Sicoob Chatbot", width="small")
    def changelog_user(self):
        with open("./logos/ChangelogUser.md", encoding="utf-8") as f:
            st.markdown(f"{f.read()}", unsafe_allow_html=True)

    def get_current_style(self):
        theme_style = self.dark_style if st.session_state.theme == "dark" else self.light_style
        return f"<style>{self.base_style}{theme_style}</style>"

    def stream_chat(self, user_input):
        """
        Faz a comunicação com o backend e retorna a resposta como streaming de tokens.
        """
        try:
            response = requests.post(
                self.backend_url,
                json={"message": user_input},
                stream=True  # Ativa o streaming
            )
            response.raise_for_status()

            # Gera os tokens conforme chegam no streaming
            for chunk in response.iter_content(chunk_size=512):
                if chunk:
                    yield chunk.decode("utf-8")
        except Exception as e:
            yield f"Erro ao conectar ao servidor: {e}"

    def clear_chat_history(self):
        st.session_state.chat_history = []
        st.toast("Histórico limpo com sucesso!", icon="✅")

    def render_sidebar(self):
        """Siderbar"""
        with st.sidebar:
            # st.button("Light/Dark Mode", on_click=self.change_style)
            if st.session_state.theme == "light":
                st.logo("./logos/sicoob-logo-horizontal-light.png", icon_image="./logos/sicoob-logo-vertical-sm.png")
            else:
                st.logo("./logos/sicoob-logo-horizontal-dark.png", icon_image="./logos/sicoob-logo-vertical-sm.png")
            if st.button("Limpar Histórico", icon=":material/delete:"):
                self.clear_chat_history()
            st.button("Atualizações", icon=":material/info:", on_click=self.changelog_user)
            st.divider()

    def add_link_to_text(self, text):
        """
        Adiciona links ao texto com base no padrão ||texto||.
        """
        return re.sub(r'\|\|(.*?)\|\|', lambda match: f' <br>Fonte: <a href="{search_file_in_drive(match.group(1))}" target="_blank">{match.group(1)}</a>', text)

    def render(self):
        """
        Renderiza a interface do chatbot.
        """
        st.markdown(self.get_current_style(), unsafe_allow_html=True)
        with open(self.configyml) as file:
            config = yaml.load(file, Loader=SafeLoader)

        authenticator = stauth.Authenticate(
            config['credentials'],
            config['cookie']['name'],
            config['cookie']['key'],
            config['cookie']['expiry_days']
        )

        with open('./config.yaml', 'w', encoding='utf-8') as file:
            yaml.dump(config, file, default_flow_style=False)

        authentication_status = authenticator.login(fields={'Form name': 'Autenticação', 'Username': 'Nome de Usuário', 'Password': 'Senha', 'Login': 'Entrar'})

        if st.session_state["authentication_status"]:
            # Renderiza a barra lateral
            self.render_sidebar()

            # Título e descrição
            st.title(self.title)
            st.write(self.description)
            with st.sidebar:
                st.caption(self.caption)
                authenticator.logout('Sair', 'main')
            # Inicializa o histórico na sessão
            if "chat_history" not in st.session_state:
                st.session_state.chat_history = []

            # Renderiza as mensagens do histórico
            for message in st.session_state.chat_history:
                role, text = message.split(":", 1)
                with st.chat_message(role.strip().lower()):
                    st.markdown(text.strip(), unsafe_allow_html=True)

            # Captura o input do usuário
            user_input = st.chat_input(placeholder="Digite sua pergunta...")
            if user_input:
                # Exibe a mensagem do usuário
                with st.chat_message("user"):
                    st.write(user_input)
                st.session_state.chat_history.append(f"user: {user_input}")
                # Placeholder para a resposta do assistente
                with st.chat_message("assistant"):
                    message_placeholder = st.empty()
                    assistant_message = ""
                    try:
                        # Gerando ID único para a mensagem
                        message_id = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
                        print(f"Message ID gerado: {message_id}")
                        # Executa o streaming de tokens enquanto o backend responde
                        for token in self.stream_chat(user_input):
                            assistant_message += token
                            message_placeholder.markdown(assistant_message + "▌", unsafe_allow_html=True)
                    except Exception as e:
                        st.error(f"Erro durante o chat: {str(e)}")
                        print(f"Erro durante o chat: {str(e)}")
                    finally:
                        assistant_message_with_link = self.add_link_to_text(assistant_message)
                        message_placeholder.markdown(assistant_message_with_link, unsafe_allow_html=True)
                        # Feedback
                        self.feedback_manager.render_feedback_buttons(
                            message_id=message_id,
                            user_input=user_input,
                            assistant_response=assistant_message_with_link
                        )
                        # Adicionando histórico Streamlit
                        st.session_state.chat_history.append(f"assistant: {assistant_message_with_link}")
        elif st.session_state["authentication_status"] == False:
            st.error('Nome de Usuário/Senha incorreta.')
        elif st.session_state["authentication_status"] == None:
            st.warning('Por favor entre com seu nome de usuário e senha.')


if __name__ == "__main__":
    chatbot_app = ChatbotApp()
    chatbot_app.render()