import os
from typing import Optional, Tuple
import gradio as gr
from langchain_community.document_loaders import PyPDFLoader, DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
import torch
import tempfile
import time

# Configurações
LLM_MODEL = "google/flan-t5-large"
DOCS_DIR = "documents"

class DocumentQA:
    def __init__(self):
        # Carrega o modelo e o tokenizador
        self.tokenizer = AutoTokenizer.from_pretrained(LLM_MODEL)
        self.model = AutoModelForSeq2SeqLM.from_pretrained(
            LLM_MODEL,
            device_map="auto",
            torch_dtype=torch.float32
        )
        
        # Carrega a base de conhecimento
        self.base_texts = self.load_base_knowledge()

    def load_base_knowledge(self) -> Optional[list]:
        try:
            if not os.path.exists(DOCS_DIR):
                os.makedirs(DOCS_DIR)
                return None

            # Carrega documentos da pasta
            loader = DirectoryLoader(
                DOCS_DIR,
                glob="**/*.pdf",
                loader_cls=PyPDFLoader
            )
            documents = loader.load()
            
            if not documents:
                return None
            
            # Divide os documentos em trechos menores
            text_splitter = RecursiveCharacterTextSplitter(
                chunk_size=500,
                chunk_overlap=100,
                length_function=len,
                separators=["\n\n", "\n", ".", " ", ""]
            )
            texts = text_splitter.split_documents(documents)
            
            # Extrai o texto dos trechos
            return [doc.page_content for doc in texts]
            
        except Exception as e:
            print(f"Erro ao carregar base de conhecimento: {str(e)}")
            return None

    def process_pdf(self, file_content: bytes) -> Optional[list]:
        try:
            # Salva o PDF temporariamente
            with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as tmp_file:
                tmp_file.write(file_content)
                tmp_path = tmp_file.name

            # Carrega o PDF
            loader = PyPDFLoader(tmp_path)
            documents = loader.load()
            os.unlink(tmp_path)
            
            if not documents:
                return None
            
            # Divide o PDF em trechos menores
            text_splitter = RecursiveCharacterTextSplitter(
                chunk_size=500,
                chunk_overlap=100,
                length_function=len,
                separators=["\n\n", "\n", ".", " ", ""]
            )
            texts = text_splitter.split_documents(documents)
            
            # Extrai o texto dos trechos
            return [doc.page_content for doc in texts]
            
        except Exception as e:
            print(f"Erro ao processar PDF: {str(e)}")
            return None

    def find_relevant_texts(self, query: str, texts: list) -> list:
        """Encontra trechos relevantes com base em palavras-chave da pergunta."""
        relevant_texts = []
        query_keywords = set(query.lower().split())
        
        for text in texts:
            text_keywords = set(text.lower().split())
            if query_keywords.intersection(text_keywords):
                relevant_texts.append(text)
        
        return relevant_texts

    def generate_response(self, file_obj, query: str, progress=gr.Progress()) -> Tuple[str, str, str]:
        """Retorna (resposta, status, tempo_decorrido)"""
        if not query.strip():
            return "Por favor, insira uma pergunta.", "⚠️ Aguardando pergunta", "0s"
        
        start_time = time.time()
        try:
            progress(0.2, desc="Processando documentos...")
            
            # Determina a fonte dos documentos
            has_pdf = file_obj is not None
            has_base = self.base_texts is not None
            source_type = "both" if has_pdf and has_base else "pdf" if has_pdf else "base" if has_base else None
            
            if not source_type:
                return "Nenhuma fonte de documentos disponível.", "❌ Sem documentos", "0s"
            
            # Processa documento
            if has_pdf:
                pdf_texts = self.process_pdf(file_obj)
                if pdf_texts is None:
                    return "Não foi possível processar o PDF.", "❌ Erro no processamento", "0s"
            else:
                pdf_texts = []
            
            # Combina os textos
            all_texts = pdf_texts + (self.base_texts if self.base_texts else [])
            
            progress(0.4, desc="Buscando informações relevantes...")
            
            # Encontra trechos relevantes
            relevant_texts = self.find_relevant_texts(query, all_texts)
            
            # Verifica se há trechos relevantes
            if not relevant_texts:
                return "🔍 Não foram encontradas informações suficientes nos documentos para responder esta pergunta.", "⚠️ Contexto insuficiente", f"{time.time() - start_time:.1f}s"
            
            # Prepara o contexto para o prompt
            context = "\n\n".join(relevant_texts)
            
            progress(0.6, desc="Gerando resposta...")
            
            # Cria o prompt
            prompt = f"""Instruções:
            1. Analise cuidadosamente o contexto fornecido.
            2. Responda à seguinte pergunta em português de forma clara e direta: {query}
            3. Use apenas informações encontradas no contexto.
            4. Se não houver informações suficientes, indique explicitamente.
            5. Mantenha a resposta objetiva e baseada em fatos.
            6. Cite exemplos específicos do contexto quando relevante.

            Contexto:
            {context}

            Pergunta: {query}"""
            
            # Gera a resposta usando o modelo diretamente
            inputs = self.tokenizer(prompt, return_tensors="pt", max_length=512, truncation=True)
            outputs = self.model.generate(
                inputs["input_ids"],
                max_length=512,
                temperature=0.3,
                top_p=0.9,
                repetition_penalty=1.2
            )
            response = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
            
            elapsed_time = f"{time.time() - start_time:.1f}s"
            progress(1.0, desc="Concluído!")
            
            return response, "✅ Sucesso", elapsed_time
            
        except Exception as e:
            elapsed_time = f"{time.time() - start_time:.1f}s"
            return f"Erro ao gerar resposta: {str(e)}", "❌ Erro", elapsed_time

def create_demo():
    qa_system = DocumentQA()
    
    with gr.Blocks(theme=gr.themes.Soft()) as demo:
        with gr.Column(elem_id="container"):
            # Cabeçalho
            gr.Markdown(
                """
                # 🤖 Assistente de Documentos Inteligente
                
                Sistema de consulta avançada que responde perguntas sobre seus documentos.
                """
            )
            
            # Área principal
            with gr.Row():
                # Coluna de entrada
                with gr.Column():
                    with gr.Group():
                        gr.Markdown("### 📄 Documentos")
                        file_input = gr.File(
                            label="Upload de PDF (opcional)",
                            type="binary",
                            file_types=[".pdf"],
                            height=100,
                        )
                        info = gr.Markdown(
                            f"""
                            ℹ️ O sistema consulta:
                            - PDFs enviados por você
                            - Documentos na pasta `{DOCS_DIR}`
                            """
                        )
                    
                    with gr.Group():
                        gr.Markdown("### ❓ Sua Pergunta")
                        query_input = gr.Textbox(
                            placeholder="Digite sua pergunta sobre os documentos...",
                            lines=3,
                            max_lines=6,
                            show_label=False,
                        )
                    
                    with gr.Row():
                        clear_btn = gr.Button("🗑️ Limpar", variant="secondary")
                        submit_btn = gr.Button("🔍 Enviar Pergunta", variant="primary")
                
                # Coluna de saída
                with gr.Column():
                    with gr.Group():
                        gr.Markdown("### 📝 Resposta")
                        with gr.Row():
                            status_output = gr.Textbox(
                                label="Status",
                                value="⏳ Aguardando...",
                                interactive=False,
                                show_label=False,
                            )
                            time_output = gr.Textbox(
                                label="Tempo",
                                value="0s",
                                interactive=False,
                                show_label=False,
                            )
                        
                        response_output = gr.Textbox(
                            label="Resposta",
                            placeholder="A resposta aparecerá aqui...",
                            interactive=False,
                            lines=12,
                            show_label=False,
                        )
            
            # Exemplos
            with gr.Accordion("📚 Exemplos de Perguntas", open=False):
                gr.Examples(
                    examples=[
                        [None, "Quais são os principais tópicos abordados neste documento?"],
                        [None, "Resuma as conclusões mais importantes."],
                        [None, "O que o documento diz sobre [tema específico]?"],
                        [None, "Quais são as recomendações apresentadas?"],
                    ],
                    inputs=[file_input, query_input],
                )
            
            # Rodapé
            gr.Markdown(
                """
                ---
                ### 🔧 Informações do Sistema
                * Respostas geradas usando tecnologia de processamento de linguagem natural
                * Processamento inteligente de documentos PDF
                * Respostas baseadas exclusivamente no conteúdo dos documentos
                * Suporte a múltiplos documentos e contextos
                """
            )
        
        # Eventos
        submit_btn.click(
            fn=qa_system.generate_response,
            inputs=[file_input, query_input],
            outputs=[response_output, status_output, time_output],
        )
        
        clear_btn.click(
            lambda: (None, "", "⏳ Aguardando...", "0s"),
            outputs=[file_input, query_input, status_output, time_output],
        )
        
        # Limpa a resposta quando a pergunta muda
        query_input.change(
            lambda: ("", "⏳ Aguardando...", "0s"),
            outputs=[response_output, status_output, time_output],
        )
    
    return demo

if __name__ == "__main__":
    demo = create_demo()
    demo.launch()