import os
import re
import time
import json
import requests

from dotenv import load_dotenv
load_dotenv()
from sqlalchemy import text
from datetime import datetime
from src.extensions import db
from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
from werkzeug.utils import secure_filename
from src.services.chat_service import ChatService
from flask_login import login_required, current_user
from src.utils import read_document_content, allowed_file 
from src.services.sharepoint_client import SharePointClient
from src.discord.discord_webhook import send_discord_audit_log
from src.models import Agente, Chat, AgenteTreino, Message, ChatFile
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify, session, current_app, send_from_directory

chat_bp = Blueprint('chat', __name__)

@chat_bp.route('/health', methods=['GET'])
def health_check():
    """Endpoint para verificar a saúde do servidor"""
    try:
        try:
            db.session.execute(text('SELECT 1'))
            db_status = 'connected'
        except Exception as db_error:
            current_app.logger.error(f"Database health check failed: {str(db_error)}")
            db_status = 'disconnected'

        ollama_url = os.getenv('ollama_url')
        backup_url = os.getenv('backup_url')
        ollama_status = 'disconnected'
        
        try:
            response = requests.get(f"{ollama_url}/api/health", timeout=5)
            if response.ok:
                ollama_status = 'connected'
        except requests.RequestException:
            try:
                response = requests.get(f"{backup_url}/api/health", timeout=5)
                if response.ok:
                    ollama_status = 'connected (backup)'
            except requests.RequestException:
                ollama_status = 'disconnected'
        
        if db_status == 'disconnected':
            status = 'unhealthy'
            status_code = 500
        elif ollama_status == 'disconnected':
            status = 'degraded'
            status_code = 503
        else:
            status = 'healthy'
            status_code = 200
            
        return jsonify({
            'status': status,
            'ollama': ollama_status,
            'database': db_status,
            'timestamp': datetime.now().isoformat()
        }), status_code
                
    except Exception as e:
        current_app.logger.error(f"Health check failed: {str(e)}")
        return jsonify({
            'status': 'unhealthy',
            'error': str(e),
            'timestamp': datetime.now().isoformat()
        }), 500

@chat_bp.route('/chat', methods=['POST'])
@login_required
def chat():
    try:
        print('[DEBUG] Iniciando processamento de mensagem...')
        message = request.form.get('message', '').strip()
        files = request.files.getlist('files')
        chat_id = request.form.get('chat_id')
        agent_id = request.form.get('agent_id')
        print(f'[DEBUG] Dados recebidos: message={message}, chat_id={chat_id}, agent_id={agent_id}')
        print(f'[DEBUG] Arquivos recebidos: {[f.filename for f in files]}')
        if not message and not files:
            return jsonify({'error': 'Mensagem ou arquivo é obrigatório'}), 400
        if not agent_id:
            return jsonify({'error': 'ID do agente é obrigatório'}), 400

        print(f'[DEBUG] Buscando agente com ID: {agent_id}')
        agent = Agente.query.get(agent_id)
        if not agent:
            print(f'[ERROR] Agente não encontrado: {agent_id}')
            return jsonify({'error': 'Agente não encontrado'}), 404

        print(f'[DEBUG] Agente encontrado: {getattr(agent, 'nome', 'N/A')}')

        access_token = session.get('access_token')
        is_llama = (getattr(agent, 'model_identifier', None) and 'llama' in agent.model_identifier.lower()) or (getattr(agent, 'nome', '').lower() == 'llama')
        if access_token and is_llama and re.search(r'\bsharepoint\b', message, re.IGNORECASE):
            intent_prompt = f"""
Você é um assistente que interpreta comandos de SharePoint. Dada a mensagem do usuário, responda apenas em JSON, indicando a intenção e o nome da pasta (se houver):
Exemplos:
Usuário: "quais pastas tem no sharepoint?"
Resposta: {{"intent": "listar_pastas", "pasta": null}}
Usuário: "me mostre a pasta Sao Paulo"
Resposta: {{"intent": "buscar_pasta", "pasta": "Sao Paulo"}}
Usuário: "ela não está na raiz"
Resposta: {{"intent": "buscar_em_subpastas", "pasta": "Sao Paulo"}}
Usuário: "quero ver o arquivo relatório.pdf"
Resposta: {{"intent": "buscar_arquivo", "arquivo": "relatório.pdf"}}
Mensagem do usuário: "{message}"
Responda apenas o JSON, sem explicações.
"""
            ollama_url = 'http://dns.auditto.com.br:11434/api/chat'
            payload = {
                'model': 'llama3:latest',
                'messages': [
                    {'role': 'system', 'content': intent_prompt}
                ],
                'stream': False,
                'temperature': 0.0,
                'top_p': 1.0,
                'num_predict': 256
            }
            try:
                response = requests.post(ollama_url, json=payload, timeout=(5, 30))
                response_data = response.json()
                intent_json = response_data.get('message', {}).get('content', '{}')
                import json as _json
                intent = _json.loads(intent_json)
            except Exception as e:
                print(f'[ERROR] Erro ao interpretar intenção com IA: {str(e)}')
                intent = {}
            if intent.get('intent') == 'listar_pastas':
                sp_client = SharePointClient(access_token=access_token)
                raiz = sp_client.list_files(drive_name='Arquivos')
                print('[DEBUG] JSON bruto da raiz do SharePoint:', raiz)
                itens = raiz.get('value', [])
                if not itens:
                    resposta = 'A raiz da biblioteca está vazia. Nenhuma pasta ou arquivo encontrado.'
                else:
                    nomes = []
                    for item in itens:
                        tipo = '📁' if item.get('folder') else '📄'
                        nomes.append(f"{tipo} {item['name']}")
                    resposta = f'Itens disponíveis na raiz do SharePoint:\n' + '\n'.join(nomes)
                if chat_id:
                    ChatService.add_message(chat_id, resposta, False)
                return jsonify({'response': resposta, 'chat_id': chat_id})
            elif intent.get('intent') == 'buscar_pasta' and intent.get('pasta'):
                pasta = intent['pasta']
                session['last_sharepoint_folder'] = pasta
                sp_client = SharePointClient(access_token=access_token)
                try:
                    arquivos = sp_client.list_files(pasta, drive_name='Arquivos')
                    itens = arquivos.get('value', [])
                    nomes = []
                    for item in itens[:30]:
                        tipo = '📁' if item.get('folder') else '📄'
                        nomes.append(f"{tipo} {item['name']}")
                    if nomes:
                        resposta = f'Conteúdo real da pasta {pasta} do SharePoint:\n' + '\n'.join(nomes)
                    else:
                        resposta = f'A pasta {pasta} está vazia.'
                    system_prompt = f"Você é um assistente. O usuário pediu para listar o conteúdo da pasta '{pasta}' do SharePoint.\n\nItens encontrados:\n{chr(10).join(nomes)}\n\nResponda de forma útil sobre esses itens."
                    payload = {
                        'model': 'llama3:latest',
                        'messages': [
                            {'role': 'system', 'content': system_prompt},
                            {'role': 'user', 'content': message}
                        ],
                        'stream': False,
                        'temperature': float(agent.temperature or 0.7),
                        'top_p': float(agent.top_p or 0.9),
                        'num_predict': int(agent.max_tokens or 2000)
                    }
                    http_session = requests.Session()
                    retry_strategy = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
                    adapter = HTTPAdapter(max_retries=retry_strategy)
                    http_session.mount("http://", adapter)
                    http_session.mount("https://", adapter)
                    ollama_url = 'http://dns.auditto.com.br:11434/api/chat'
                    try:
                        response = http_session.post(ollama_url, json=payload, timeout=(5, 30))
                        if response.status_code == 200:
                            response_data = response.json()
                            ia_resposta = response_data.get('message', {}).get('content', '')
                            resposta += f"\n\nResposta da IA sobre o conteúdo:\n{ia_resposta}"
                    except Exception as e:
                        resposta += f"\n[Erro ao consultar a IA sobre o conteúdo: {str(e)}]"
                except requests.HTTPError as e:
                    if hasattr(e, 'response') and e.response is not None and e.response.status_code == 404:
                        try:
                            caminho_real = sp_client.find_folder_recursive(pasta, drive_name='Arquivos')
                        except Exception as e:
                            resposta = f'Erro ao buscar a pasta: {str(e)}'
                            if chat_id:
                                ChatService.add_message(chat_id, resposta, False)
                            return jsonify({'response': resposta, 'chat_id': chat_id})
                        if caminho_real:
                            tree_lines = sp_client.list_tree(caminho_real, drive_name='Arquivos')
                            if tree_lines:
                                resposta = f'Conteúdo da pasta {caminho_real} do SharePoint (em árvore):\n' + '\n'.join(tree_lines)
                            else:
                                resposta = f'A pasta {caminho_real} está vazia.'
                        else:
                            try:
                                fuzzy_path, fuzzy_sugestoes = sp_client.find_folder_fuzzy(pasta, drive_name='Arquivos')
                            except Exception as e:
                                resposta = f'Erro ao buscar pastas semelhantes: {str(e)}'
                                if chat_id:
                                    ChatService.add_message(chat_id, resposta, False)
                                return jsonify({'response': resposta, 'chat_id': chat_id})
                            if fuzzy_path:
                                tree_lines = sp_client.list_tree(fuzzy_path, drive_name='Arquivos')
                                resposta = f'Pasta não encontrada exatamente, mas encontrei uma pasta parecida: {fuzzy_path}\n\nConteúdo:\n' + '\n'.join(tree_lines)
                                if fuzzy_sugestoes:
                                    resposta += f'\nSugestões de nomes parecidos: {", ".join(fuzzy_sugestoes)}'
                            else:
                                resposta = f'A pasta "{pasta}" não foi encontrada no SharePoint. Pastas disponíveis na raiz podem ser muitas ou a busca foi interrompida por limite de pastas visitadas. Tente um nome mais específico ou navegue por níveis.'
                        if chat_id:
                            ChatService.add_message(chat_id, resposta, False)
                        return jsonify({'response': resposta, 'chat_id': chat_id})
                    else:
                        resposta = f'Erro inesperado ao acessar o SharePoint: {str(e)}'
                        if chat_id:
                            ChatService.add_message(chat_id, resposta, False)
                        return jsonify({'response': resposta, 'chat_id': chat_id})
                except Exception as e:
                    resposta = f'Erro inesperado ao buscar pasta: {str(e)}'
                    if chat_id:
                        ChatService.add_message(chat_id, resposta, False)
                    return jsonify({'response': resposta, 'chat_id': chat_id})
                if chat_id:
                    ChatService.add_message(chat_id, resposta, False)
                return jsonify({'response': resposta, 'chat_id': chat_id})
            elif intent.get('intent') == 'buscar_em_subpastas' and intent.get('pasta'):
                pasta = intent['pasta']
                sp_client = SharePointClient(access_token=access_token)
                try:
                    caminho_real = sp_client.find_folder_recursive(pasta, drive_name='Arquivos')
                except Exception as e:
                    resposta = f'Erro ao buscar subpastas: {str(e)}'
                    if chat_id:
                        ChatService.add_message(chat_id, resposta, False)
                    return jsonify({'response': resposta, 'chat_id': chat_id})
                if caminho_real:
                    arquivos = sp_client.list_files(caminho_real, drive_name='Arquivos')
                    itens = arquivos.get('value', [])
                    nomes = []
                    for item in itens[:30]:
                        tipo = '📁' if item.get('folder') else '📄'
                        nomes.append(f"{tipo} {item['name']}")
                    if nomes:
                        resposta = f'Conteúdo real da pasta {caminho_real} do SharePoint:\n' + '\n'.join(nomes)
                    else:
                        resposta = f'A pasta {caminho_real} está vazia.'
                    system_prompt = f"Você é um assistente. O usuário pediu para listar o conteúdo da pasta '{caminho_real}' do SharePoint.\n\nItens encontrados:\n{chr(10).join(nomes)}\n\nResponda de forma útil sobre esses itens."
                    payload = {
                        'model': 'llama3:latest',
                        'messages': [
                            {'role': 'system', 'content': system_prompt},
                            {'role': 'user', 'content': message}
                        ],
                        'stream': False,
                        'temperature': float(agent.temperature or 0.7),
                        'top_p': float(agent.top_p or 0.9),
                        'num_predict': int(agent.max_tokens or 2000)
                    }
                    http_session = requests.Session()
                    retry_strategy = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
                    adapter = HTTPAdapter(max_retries=retry_strategy)
                    http_session.mount("http://", adapter)
                    http_session.mount("https://", adapter)
                    ollama_url = 'http://dns.auditto.com.br:11434/api/chat'
                    try:
                        response = http_session.post(ollama_url, json=payload, timeout=(5, 30))
                        if response.status_code == 200:
                            response_data = response.json()
                            ia_resposta = response_data.get('message', {}).get('content', '')
                            resposta += f"\n\nResposta da IA sobre o conteúdo:\n{ia_resposta}"
                    except Exception as e:
                        resposta += f"\n[Erro ao consultar a IA sobre o conteúdo: {str(e)}]"
                else:
                    try:
                        fuzzy_path, fuzzy_sugestoes = sp_client.find_folder_fuzzy(pasta, drive_name='Arquivos')
                    except Exception as e:
                        resposta = f'Erro ao buscar pastas semelhantes: {str(e)}'
                        if chat_id:
                            ChatService.add_message(chat_id, resposta, False)
                        return jsonify({'response': resposta, 'chat_id': chat_id})
                    if fuzzy_path:
                        tree_lines = sp_client.list_tree(fuzzy_path, drive_name='Arquivos')
                        resposta = f'Pasta não encontrada exatamente, mas encontrei uma pasta parecida: {fuzzy_path}\n\nConteúdo:\n' + '\n'.join(tree_lines)
                        if fuzzy_sugestoes:
                            resposta += f'\nSugestões de nomes parecidos: {", ".join(fuzzy_sugestoes)}'
                    else:
                        resposta = f'A pasta "{pasta}" não foi encontrada em subpastas. A busca pode ter sido interrompida por limite de pastas visitadas. Tente um nome mais específico ou navegue por níveis.'
                if chat_id:
                    ChatService.add_message(chat_id, resposta, False)
                return jsonify({'response': resposta, 'chat_id': chat_id})
            elif intent.get('intent') == 'buscar_arquivo' and intent.get('arquivo'):
                arquivo = intent['arquivo']
                pasta = session.get('last_sharepoint_folder')
                sp_client = SharePointClient(access_token=access_token)
                if pasta:
                    arquivos = sp_client.list_files(pasta, drive_name='Arquivos')
                    nomes_arquivos = [item['name'] for item in arquivos.get('value', [])]
                    if arquivo in nomes_arquivos:
                        file_path = f"{pasta}/{arquivo}"
                        conteudo = sp_client.get_file_content(file_path, drive_name='Arquivos')
                        resposta = f'Arquivo "{arquivo}" encontrado na pasta {pasta}.\n\nConteúdo do arquivo:\n{conteudo[:2000]}'
                        if chat_id:
                            ChatService.add_message(chat_id, resposta, False)
                        system_prompt = f"Você é um assistente. O usuário pediu para analisar o arquivo '{arquivo}' da pasta '{pasta}' do SharePoint.\n\nConteúdo do arquivo:\n{conteudo[:4000]}\n\nResponda de forma útil sobre esse conteúdo."
                        payload = {
                            'model': 'llama3:latest',
                            'messages': [
                                {'role': 'system', 'content': system_prompt},
                                {'role': 'user', 'content': message}
                            ],
                            'stream': False,
                            'temperature': float(agent.temperature or 0.7),
                            'top_p': float(agent.top_p or 0.9),
                            'num_predict': int(agent.max_tokens or 2000)
                        }
                        http_session = requests.Session()
                        retry_strategy = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
                        adapter = HTTPAdapter(max_retries=retry_strategy)
                        http_session.mount("http://", adapter)
                        http_session.mount("https://", adapter)
                        ollama_url = 'http://dns.auditto.com.br:11434/api/chat'
                        try:
                            response = http_session.post(ollama_url, json=payload, timeout=(5, 30))
                            if response.status_code == 200:
                                response_data = response.json()
                                ia_resposta = response_data.get('message', {}).get('content', '')
                                resposta += f"\n\nResposta da IA sobre o arquivo:\n{ia_resposta}"
                        except Exception as e:
                            resposta += f"\n[Erro ao consultar a IA sobre o arquivo: {str(e)}]"
                    else:
                        resposta = f'Arquivo "{arquivo}" não encontrado na pasta {pasta}.'
                else:
                    resposta = 'Por favor, acesse uma pasta antes de buscar um arquivo.'
                if chat_id:
                    ChatService.add_message(chat_id, resposta, False)
                return jsonify({'response': resposta, 'chat_id': chat_id})
        is_gemma = (getattr(agent, 'model_identifier', None) and 'gemma3:27b' in agent.model_identifier.lower())
        if is_gemma:
            import base64
            import tempfile
            if files and files[0] and files[0].filename:
                img_file = files[0]
                with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as tmp:
                    img_file.save(tmp.name)
                    with open(tmp.name, 'rb') as f:
                        img_bytes = f.read()
                        img_b64 = base64.b64encode(img_bytes).decode('utf-8')
                payload = {
                    'model': 'gemma3:27b',
                    'messages': [
                        {'role': 'user', 'content': message, 'images': [img_b64]}
                    ],
                    'stream': False,
                    'temperature': float(agent.temperature or 0.7),
                    'top_p': float(agent.top_p or 0.9),
                    'num_predict': int(agent.max_tokens or 2000)
                }
                ollama_url = 'http://dns.auditto.com.br:11434/api/chat'
                response = requests.post(ollama_url, json=payload, timeout=(10, 60))
                response_data = response.json()
                response_text = response_data.get('message', {}).get('content', '')
                if chat_id:
                    ChatService.add_message(chat_id, '[imagem enviada]', True)
                    ChatService.add_message(chat_id, response_text, False)
                return jsonify({'response': response_text, 'chat_id': chat_id})
            if message.lower().startswith('/img') or message.lower().startswith('gerar imagem:'):
                prompt = message.replace('/img', '').replace('gerar imagem:', '').strip()
                payload = {
                    'model': 'gemma3:27b',
                    'prompt': prompt,
                    'format': 'image',
                    'stream': False,
                    'temperature': float(agent.temperature or 0.7),
                    'top_p': float(agent.top_p or 0.9),
                    'num_predict': int(agent.max_tokens or 2000)
                }
                ollama_url = 'http://dns.auditto.com.br:11434/api/generate'
                response = requests.post(ollama_url, json=payload, timeout=(10, 60))
                response_data = response.json()
                image_b64 = response_data.get('image', None)
                if image_b64:
                    if chat_id:
                        ChatService.add_message(chat_id, '[imagem gerada]', False)
                    return jsonify({'image_base64': image_b64, 'chat_id': chat_id})
                else:
                    return jsonify({'error': 'Falha ao gerar imagem'}), 500

        anexos_texto = []
        for f in files:
            if f and f.filename and allowed_file(f.filename):
                import tempfile
                with tempfile.NamedTemporaryFile(delete=False) as tmp:
                    f.save(tmp.name)
                    texto = read_document_content(tmp.name, f.filename)
                    if texto and not texto.startswith('[ERRO'):
                        anexos_texto.append(f"\n### Conteúdo do anexo: {f.filename}\n{texto}\n### Fim do anexo: {f.filename}")

        if not chat_id:
            chat, error = ChatService.create_chat(agent_id)
            if error or not chat:
                print(f'[ERROR] Erro ao criar chat: {error}')
                return jsonify({'error': 'Erro ao criar chat'}), 500
            chat_id = chat.id
            print(f'[DEBUG] Novo chat criado: {chat_id}')

        print(f'[DEBUG] Buscando chat existente: {chat_id}')
        chat = ChatService.get_chat(chat_id)
        if not chat or getattr(chat, 'user_id', None) != current_user.id:
            print(f'[ERROR] Chat não encontrado ou acesso negado: {chat_id}')
            return jsonify({'error': 'Chat não encontrado ou acesso negado'}), 404

        print(f'[DEBUG] Chat ID: {getattr(chat, 'id', 'N/A')}')

        print('[DEBUG] Salvando mensagem do usuário')
        user_message, error = ChatService.add_message(getattr(chat, 'id', None), message, True)
        if error:
            print(f'[ERROR] Erro ao salvar mensagem do usuário: {error}')
            return jsonify({'error': error}), 500

        if files:
            for f in files:
                if f and f.filename:
                    send_discord_audit_log(
                        action="Upload de arquivo",
                        user_id=current_user.id,
                        object_type="Arquivo de Chat",
                        object_id=chat_id,
                        details=f"Arquivo enviado: {f.filename} ({getattr(f, 'content_length', 'N/A')} bytes)",
                        ip_address=request.remote_addr
                    )

        print('[DEBUG] Buscando histórico do chat')
        chat_history = []
        if chat and getattr(chat, 'messages', None):
            try:
                messages_iter = chat.messages if isinstance(chat.messages, list) else []
                recent_messages = sorted(
                    messages_iter,
                    key=lambda m: getattr(m, 'created_at', None) or __import__('datetime').datetime.min
                )[-10:]
            except Exception as e:
                print(f'[WARNING] Erro ao ordenar mensagens: {str(e)}')
                recent_messages = []
            chat_history = [
                {
                    'role': 'user' if getattr(msg, 'is_user', False) else 'assistant',
                    'content': getattr(msg, 'message', '')
                }
                for msg in recent_messages
            ]
            print(f'[DEBUG] Histórico carregado: {len(chat_history)} mensagens')
            print('[DEBUG] Histórico:', json.dumps(chat_history, indent=2))

        print('[DEBUG] Buscando arquivos de treino')
        training_files_content = []
        if agent.treinos:
            for treino in agent.treinos:
                try:
                    file_path = os.path.join('uploads/agentes', str(agent.id), treino.arquivo)
                    print(f'[DEBUG] Lendo arquivo de treino: {file_path}')
                    content = read_document_content(file_path, treino.arquivo)
                    training_files_content.append(f"\n### Arquivo de Treino: {treino.arquivo}\n{content}\n### Fim do Arquivo de Treino: {treino.arquivo}")
                except Exception as e:
                    print(f'[WARNING] Erro ao ler arquivo de treino {treino.arquivo}: {str(e)}')

        system_prompt = agent.system_prompt or "Você é um assistente útil e amigável."
        if training_files_content:
            system_prompt += "\n\n" + "\n".join(training_files_content)
        if anexos_texto:
            system_prompt += "\n\n" + "\n".join(anexos_texto)

        system_prompt += """
IMPORTANTE:
- Mantenha o contexto da conversa e responda de forma coerente com as mensagens anteriores.
- Seja específico e detalhado em suas respostas.
- Mantenha um tom profissional e amigável.
"""

        print('[DEBUG] System prompt preparado')

        print(f"[DEBUG] agent.model_identifier: {agent.model_identifier}")
        if agent.model_identifier and "gemini" in agent.model_identifier.lower():
            from src.services.gemini_service import gemini_service
            # Monta prompt estruturado com histórico
            history_context = "\n".join([f"{msg['role'].upper()}: {msg['content']}" for msg in chat_history])
            full_message = f"{history_context}\n\nUSER: {message}" if history_context else message
            
            print(f"[DEBUG GEMINI] system_prompt: {system_prompt[:200]}...")
            print(f"[DEBUG GEMINI] full_message: {full_message}")
            
            gemini_result = gemini_service.generate_response(
                message=full_message,
                system_prompt=system_prompt,
                temperature=float(agent.temperature or 0.7),
                max_tokens=int(agent.max_tokens or 2000),
                top_p=float(agent.top_p or 0.9),
                model=agent.model_identifier
            )
            response_text = gemini_result["response"] if isinstance(gemini_result, dict) and "response" in gemini_result else gemini_result
            ChatService.add_message(chat.id, response_text, False)
            return jsonify({'response': response_text, 'chat_id': chat.id})
        payload = {
            'model': agent.model_identifier or 'llama3:latest',  
            'messages': [
                {'role': 'system', 'content': system_prompt},
                *chat_history,  
                {'role': 'user', 'content': message}
            ],
            'stream': False,
            'temperature': float(agent.temperature or 0.7),
            'top_p': float(agent.top_p or 0.9),
            'num_predict': int(agent.max_tokens or 2000)
        }

        print('[DEBUG] Payload para Ollama:', json.dumps(payload, indent=2))

        http_session = requests.Session()
        retry_strategy = Retry(
            total=3,  
            backoff_factor=1, 
            status_forcelist=[500, 502, 503, 504]
        )
        adapter = HTTPAdapter(max_retries=retry_strategy)
        http_session.mount("http://", adapter)
        http_session.mount("https://", adapter)

        primary_ollama_url = 'http://192.168.8.250:11434/api/chat'
        backup_ollama_url = 'http://dns.auditto.com.br:11434/api/chat'  
        
        ollama_urls = [primary_ollama_url, backup_ollama_url]
        
        for i, ollama_url in enumerate(ollama_urls):
            try:
                print(f'[DEBUG] Tentativa {i+1}: Enviando requisição para Ollama em {ollama_url}')
                current_app.logger.info(f'[DEBUG] Tentativa {i+1}: Enviando requisição para Ollama: {ollama_url}')
                current_app.logger.debug(f'[DEBUG] Payload: {json.dumps(payload, indent=2)}')
                
                payload['stream'] = False
                
                timeout_connect = 10 if i == 0 else 15  
                timeout_read = 45 if i == 0 else 60   
                
                response = http_session.post(
                    ollama_url,
                    json=payload,
                    timeout=(timeout_connect, timeout_read),
                )
                
                print(f'[DEBUG] Resposta do Ollama - Status: {response.status_code}')
                current_app.logger.info(f'[DEBUG] Resposta do Ollama - Status: {response.status_code}')
                print(f'[DEBUG] Resposta do Ollama - Headers: {dict(response.headers)}')
                current_app.logger.debug(f'[DEBUG] Resposta do Ollama - Headers: {dict(response.headers)}')
                
                if response.status_code != 200:
                    error_msg = f'Erro na resposta do Ollama (tentativa {i+1}): {response.text}'
                    print(f'[ERROR] {error_msg}')
                    current_app.logger.error(f'[ERROR] {error_msg}')
                    
                    if i < len(ollama_urls) - 1:
                        print(f'[INFO] Tentando próximo servidor Ollama...')
                        continue
                    else:
                        return jsonify({'error': 'Erro ao processar mensagem em todos os servidores'}), 500

                try:
                    response_data = response.json()
                    if 'message' not in response_data or 'content' not in response_data['message']:
                        error_msg = f'Formato de resposta inválido do Ollama (tentativa {i+1})'
                        print(f'[ERROR] {error_msg}')
                        current_app.logger.error(f'[ERROR] {error_msg}')
                        
                        if i < len(ollama_urls) - 1:
                            continue
                        else:
                            return jsonify({'error': 'Resposta inválida do servidor da IA'}), 500

                    response_text = response_data['message']['content']
                    
                    if not response_text:
                        error_msg = f'Resposta vazia do servidor da IA (tentativa {i+1})'
                        print(f'[ERROR] {error_msg}')
                        current_app.logger.error(f'[ERROR] {error_msg}')
                        
                        if i < len(ollama_urls) - 1:
                            continue
                        else:
                            return jsonify({'error': 'Resposta vazia do servidor da IA'}), 500

                    print(f'[DEBUG] Resposta obtida com sucesso do servidor {i+1}')
                    print('[DEBUG] Salvando resposta do bot')
                    bot_message, error = ChatService.add_message(chat.id, response_text, False)
                    if error:
                        print(f'[ERROR] Erro ao salvar resposta do bot: {error}')
                        return jsonify({'error': error}), 500

                    return jsonify({
                        'response': response_text,
                        'chat_id': chat.id
                    })

                except json.JSONDecodeError as e:
                    error_msg = f'Erro ao decodificar resposta JSON (tentativa {i+1}): {str(e)}'
                    print(f'[ERROR] {error_msg}')
                    current_app.logger.error(f'[ERROR] {error_msg}')
                    
                    if i < len(ollama_urls) - 1:
                        continue
                    else:
                        return jsonify({'error': 'Erro ao processar resposta do servidor'}), 500

            except requests.Timeout as e:
                error_msg = f'Timeout na requisição para Ollama (tentativa {i+1}): {str(e)}'
                print(f'[ERROR] {error_msg}')
                current_app.logger.error(f'[ERROR] {error_msg}')
                
                if i < len(ollama_urls) - 1:
                    print(f'[INFO] Timeout no servidor {i+1}, tentando próximo servidor...')
                    continue
                else:
                    return jsonify({'error': 'Timeout ao conectar com todos os servidores da IA'}), 504
                    
            except requests.ConnectionError as e:
                error_msg = f'Erro de conexão com Ollama (tentativa {i+1}): {str(e)}'
                print(f'[ERROR] {error_msg}')
                current_app.logger.error(f'[ERROR] {error_msg}')
                
                if i < len(ollama_urls) - 1:
                    print(f'[INFO] Erro de conexão no servidor {i+1}, tentando próximo servidor...')
                    continue
                else:
                    return jsonify({'error': 'Erro de conexão com todos os servidores da IA'}), 503
                    
            except requests.RequestException as e:
                error_msg = f'Erro na requisição para Ollama (tentativa {i+1}): {str(e)}'
                print(f'[ERROR] {error_msg}')
                current_app.logger.error(f'[ERROR] {error_msg}')
                
                if i < len(ollama_urls) - 1:
                    print(f'[INFO] Erro de requisição no servidor {i+1}, tentando próximo servidor...')
                    continue
                else:
                    return jsonify({'error': 'Erro ao conectar com todos os servidores da IA'}), 503

    except Exception as e:
        print(f'[ERROR] Erro não tratado: {str(e)}')
        current_app.logger.error(f'[ERROR] Erro não tratado: {str(e)}')
        if request.is_json or request.accept_mimetypes['application/json']:
            return jsonify({'error': 'Erro interno do servidor'}), 500
        else:
            from flask import redirect, url_for
            return redirect(url_for('auth.login'))

@chat_bp.route('/chat')
@login_required
def chat_redirect():
    if hasattr(current_user, 'role') and current_user.role == 'superadmin':
        agentes = Agente.query.order_by(Agente.nome).all()
    else:
        agentes = Agente.query.filter_by(user_id=current_user.id).all()
    agent_id = request.args.get('agent_id', type=int)
    agent = None
    if agent_id:
        if hasattr(current_user, 'role') and current_user.role == 'superadmin':
            agent = Agente.query.filter_by(id=agent_id).first()
        else:
            agent = Agente.query.filter_by(id=agent_id, user_id=current_user.id).first()
    if not agent and agentes:
        agent = agentes[0]
    chats = ChatService.get_user_chats()
    chat = None
    if agent and (not chats or not any(c.agent_id == agent.id for c in chats)):
        chat, error = ChatService.create_chat(agent.id)
        if error:
            flash('Erro ao criar chat: ' + error, 'danger')
            return render_template('chat/chat.html', agent=agent, agentes=agentes, chats=chats, chat=None, current_chat=None, current_agent=agent)
        title_slug = chat.title.lower().replace(' ', '-') if chat and chat.title else f'chat-{chat.id if chat else ""}'
        return redirect(url_for('chat.view_chat', chat_id=chat.id if chat else 0, title=title_slug))
    return render_template('chat/chat.html', agent=agent, agentes=agentes, chats=chats, chat=chat, current_chat=chat, current_agent=agent)

@chat_bp.route('/chat/<int:chat_id>/<path:title>')
@login_required
def view_chat(chat_id, title):
    print(f"[DEBUG] Acessando /chat/{chat_id}/{title}")
    chat = ChatService.get_chat(chat_id)
    if not chat or chat.user_id != current_user.id:
        flash('Chat não encontrado ou acesso negado.', 'danger')
        return redirect(url_for('chat.conversation_history_page'))
    
    if chat.title != title:
        chat.title = title
        db.session.commit()
    
    agent = chat.agent
    if hasattr(current_user, 'role') and current_user.role == 'superadmin':
        agentes = Agente.query.order_by(Agente.nome).all()
    else:
        agentes = Agente.query.filter_by(user_id=current_user.id).all()
    chats = ChatService.get_user_chats()
    return render_template('chat/chat.html', agent=agent, agentes=agentes, chat=chat, chats=chats, current_chat=chat, current_agent=agent)

@chat_bp.route('/chat/<int:chat_id>')
@login_required
def view_chat_legacy(chat_id):
    chat = ChatService.get_chat(chat_id)
    if not chat or chat.user_id != current_user.id:
        flash('Chat não encontrado ou acesso negado.', 'danger')
        return redirect(url_for('chat.conversation_history_page'))
    
    title_slug = chat.title.lower().replace(' ', '-') if chat.title else f'chat-{chat_id}'
    return redirect(url_for('chat.view_chat', chat_id=chat_id, title=title_slug))

@chat_bp.route('/chat/instructions/<int:agent_id>')
@login_required
def instructions(agent_id):
    agent = Agente.query.filter_by(id=agent_id, user_id=current_user.id).first()
    if not agent:
        flash('Agente não encontrado ou acesso negado.', 'danger')
        return redirect(url_for('chat.chat_redirect'))
    return render_template('chat/instructions.html', agent=agent)

@chat_bp.route('/chat/instructions/<int:agent_id>/save', methods=['POST'])
@login_required
def save_instructions(agent_id):
    try:
        agent = Agente.query.filter_by(id=agent_id, user_id=current_user.id).first()
        if not agent:
            return jsonify({'success': False, 'error': 'Agente não encontrado ou acesso negado.'}), 404

        data = request.get_json()
        
        agent.system_prompt = data.get('system_prompt', '')
        agent.example_prompt = data.get('example_prompt', '')
        agent.temperature = float(data.get('temperature', 0.7))
        agent.max_tokens = int(data.get('max_tokens', 2000))
        
        db.session.commit()
        return jsonify({'success': True})
        
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'error': str(e)}), 500

@chat_bp.route('/chat/retrain', methods=['POST'])
@login_required
def retrain_agent():
    try:
        agent_id = request.form.get('agent_id')
        if not agent_id:
            return jsonify({'success': False, 'error': 'ID do agente não fornecido'}), 400

        agent = Agente.query.filter_by(id=agent_id, user_id=current_user.id).first()
        if not agent:
            return jsonify({'success': False, 'error': 'Agente não encontrado ou acesso negado'}), 404

        arquivos = request.files.getlist('arquivos')
        if not arquivos:
            return jsonify({'success': False, 'error': 'Nenhum arquivo enviado'}), 400

        for arquivo in arquivos:
            if arquivo and arquivo.filename:
                filename = secure_filename(arquivo.filename)
                pasta = os.path.join('uploads/agentes', str(agent.id))
                os.makedirs(pasta, exist_ok=True)
                caminho = os.path.join(pasta, filename)
                arquivo.save(caminho)
                
                treino = AgenteTreino(
                    agente_id=agent.id,
                    arquivo=filename
                )
                db.session.add(treino)
                send_discord_audit_log(
                    action="Upload de arquivo de treino",
                    user_id=current_user.id,
                    object_type="Arquivo de Treino",
                    object_id=agent.id,
                    details=f"Arquivo de treino enviado: {arquivo.filename}",
                    ip_address=request.remote_addr
                )

        db.session.commit()
        return jsonify({'success': True})

    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'error': str(e)}), 500
    
@chat_bp.route('/api/history/conversations', methods=['GET'])
@login_required
def api_get_conversation_history():
    """
    Endpoint da API para buscar o histórico de conversas do usuário logado e agente específico.
    """
    try:
        agent_id = request.args.get('agent_id', type=int)
        history_list, error = ChatService.get_conversation_history(agent_id=agent_id)
        if error:
            return jsonify({'error': error}), 500
        return jsonify(history_list)
    except Exception as e:
        import traceback
        print(f"[ERROR] Erro ao buscar histórico de conversas: {str(e)}")
        print(f"[ERROR] Traceback: {traceback.format_exc()}")
        return jsonify({'error': 'Erro ao buscar histórico de conversas', 'details': str(e)}), 500

@chat_bp.route('/api/chat/history/<int:chat_id>', methods=['GET'])
@login_required
def api_get_chat_history_v2(chat_id):
    print(f"[DEBUG] Acessando /api/chat/history/{chat_id}")
    try:
        chat = ChatService.get_chat(chat_id)
        if not chat:
            send_discord_audit_log(
                action="Acesso não autorizado",
                user_id=current_user.id,
                object_type="Chat",
                object_id=chat_id,
                details="Tentativa de acesso ao histórico de chat sem permissão",
                ip_address=request.remote_addr
            )
            return jsonify({'error': 'Acesso negado.'}), 403

        agent_id = request.args.get('agent_id', type=int)
        if agent_id and chat.agent_id != agent_id:
            send_discord_audit_log(
                action="Acesso negado por agent_id",
                user_id=current_user.id,
                object_type="Chat",
                object_id=chat_id,
                details=f"Tentativa de acesso ao chat de outro agente. Esperado: {agent_id}, encontrado: {chat.agent_id}",
                ip_address=request.remote_addr
            )
            return jsonify({'error': 'Chat não pertence a este agente.'}), 403

        messages = [{
            'message': msg.message,
            'is_user': msg.is_user,
            'created_at': msg.created_at.isoformat() if msg.created_at else None
        } for msg in sorted(chat.messages, key=lambda m: m.created_at)]

        print(f"[DEBUG] Retornando {len(messages)} mensagens para o chat {chat_id}")
        for i, msg in enumerate(messages):
            user_type = "USER" if msg['is_user'] else "AGENT"
            print(f"[DEBUG] Mensagem {i+1}: {user_type} - {repr(msg['message'][:100])}...")
        return jsonify(messages)
    except Exception as e:
        print(f"[ERROR] Erro ao obter histórico do chat {chat_id}: {str(e)}")
        return jsonify({'error': f'Erro ao carregar histórico: {str(e)}'}), 500

@chat_bp.route('/chat/delete/<int:chat_id>', methods=['DELETE'])
@login_required
def delete_chat(chat_id):
    """
    Endpoint para excluir um chat e todas as suas mensagens.
    """
    try:
        chat = Chat.query.filter_by(id=chat_id, user_id=current_user.id).first()
        if not chat:
            return jsonify({'error': 'Chat não encontrado ou acesso negado'}), 404
        
        Message.query.filter_by(chat_id=chat_id).delete()
        Chat.query.filter_by(id=chat_id).delete()
        send_discord_audit_log(
            action="Exclusão de chat",
            user_id=current_user.id,
            object_type="Chat",
            object_id=chat_id,
            details="Chat e mensagens excluídos",
            ip_address=request.remote_addr
        )
        return jsonify({'message': 'Chat excluído com sucesso'})
    except Exception as e:
        db.session.rollback()
        print(f"Erro ao excluir chat: {str(e)}")
        return jsonify({'error': 'Erro ao excluir chat'}), 500

@chat_bp.route('/history')
@login_required
def conversation_history_page():
    """
    Renderiza a página de histórico de conversas.
    O JavaScript nesta página fará a chamada para /api/history/conversations.
    """
    return render_template('history.html')

@chat_bp.route('/chatlog')
@login_required
def chat_log():
    chats = ChatService.get_user_chats()
    return render_template('chat/chatlog.html', chats=chats)

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in current_app.config['ALLOWED_EXTENSIONS']

@chat_bp.route('/api/chat/upload', methods=['POST'])
@login_required
def upload_chat_file():
    try:
        if 'file' not in request.files:
            current_app.logger.error("Nenhum arquivo enviado na requisição")
            return jsonify({'success': False, 'error': 'Nenhum arquivo enviado'}), 400

        file = request.files['file']
        if not file.filename:
            current_app.logger.error("Nome do arquivo não fornecido")
            return jsonify({'success': False, 'error': 'Nome do arquivo não fornecido'}), 400

        if not allowed_file(file.filename):
            current_app.logger.error(f"Tipo de arquivo não permitido: {file.filename}")
            return jsonify({
                'success': False,
                'error': f'Tipo de arquivo não permitido. Tipos permitidos: {", ".join(current_app.config["ALLOWED_EXTENSIONS"])}'
            }), 400

        if request.content_length > current_app.config['MAX_CONTENT_LENGTH']:
            current_app.logger.error(f"Arquivo muito grande: {request.content_length} bytes")
            return jsonify({
                'success': False,
                'error': f'Arquivo muito grande. Tamanho máximo permitido: {current_app.config["MAX_CONTENT_LENGTH"] / (1024*1024)}MB'
            }), 400

        upload_dir = os.path.join(current_app.config['UPLOAD_FOLDER'], 'chat')
        try:
            os.makedirs(upload_dir, exist_ok=True)
        except Exception as e:
            current_app.logger.error(f"Erro ao criar diretório de uploads: {str(e)}")
            return jsonify({'success': False, 'error': 'Erro ao criar diretório de uploads'}), 500

        filename = secure_filename(file.filename)
        file_path = os.path.join(upload_dir, filename)
        try:
            file.save(file_path)
        except Exception as e:
            current_app.logger.error(f"Erro ao salvar arquivo: {str(e)}")
            return jsonify({'success': False, 'error': 'Erro ao salvar arquivo'}), 500
        from src.utils import read_document_content
        extracted_text = read_document_content(file_path, filename)
        chat_id_value = request.form.get('chat_id')
        if chat_id_value is None or chat_id_value == '':
            current_app.logger.error("chat_id não fornecido para upload de arquivo")
            return jsonify({'success': False, 'error': 'chat_id não fornecido'}), 400
        chat_file = ChatFile(
            chat_id=int(chat_id_value),
            filename=filename,
            file_path=file_path,
            file_type=file.content_type,
            file_size=os.path.getsize(file_path)
        )
        db.session.add(chat_file)
        db.session.commit()
        send_discord_audit_log(
            action="Upload de arquivo",
            user_id=current_user.id,
            object_type="Arquivo de Chat",
            object_id=chat_id_value,
            details=f"Arquivo enviado: {filename} ({os.path.getsize(file_path)} bytes)",
            ip_address=request.remote_addr
        )

        current_app.logger.info(f"Arquivo enviado com sucesso: {filename}")
        return jsonify({
            'success': True,
            'filename': filename,
            'file_path': file_path,
            'extracted_text': extracted_text
        })

    except Exception as e:
        current_app.logger.error(f"Erro inesperado no upload de arquivo: {str(e)}")
        return jsonify({'success': False, 'error': f'Erro ao processar arquivo: {str(e)}'}), 500

@chat_bp.route('/api/chat/audio', methods=['POST'])
@login_required
def api_process_audio():
    """API endpoint para processamento de áudio"""
    try:
        if 'audio' not in request.files:
            return jsonify({'success': False, 'error': 'Nenhum arquivo de áudio enviado'}), 400

        audio_file = request.files['audio']
        chat_id = request.form.get('chat_id')
        
        if not chat_id:
            return jsonify({'success': False, 'error': 'ID do chat não fornecido'}), 400

        chat = Chat.query.get(chat_id)
        if not chat or chat.user_id != current_user.id:
            return jsonify({'success': False, 'error': 'Chat não encontrado ou acesso negado'}), 404

        audio_dir = os.path.join(current_app.config['UPLOAD_FOLDER'], str(chat_id), 'audio')
        os.makedirs(audio_dir, exist_ok=True)
        
        filename = f"audio_{int(time.time())}.wav"
        file_path = os.path.join(audio_dir, filename)
        audio_file.save(file_path)

        transcription = "Transcrição do áudio"  

        if chat_id is None or chat_id == '':
            current_app.logger.error("chat_id não fornecido para upload de áudio")
            return jsonify({'success': False, 'error': 'chat_id não fornecido'}), 400
        audio_record = ChatFile(
            chat_id=int(chat_id),
            filename=filename,
            file_path=file_path,
            file_type='audio/wav',
            file_size=os.path.getsize(file_path)
        )
        db.session.add(audio_record)
        db.session.commit()
        send_discord_audit_log(
            action="Upload de áudio",
            user_id=current_user.id,
            object_type="Áudio",
            object_id=chat_id,
            details=f"Áudio enviado: {filename} ({os.path.getsize(file_path)} bytes)",
            ip_address=request.remote_addr
        )

        return jsonify({
            'success': True,
            'transcription': transcription
        })

    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Erro no processamento de áudio: {str(e)}")
        return jsonify({'success': False, 'error': 'Erro ao processar áudio'}), 500 

@chat_bp.route('/public-chat/<public_token>')
def public_chat_iframe(public_token):
    from src.models import Agente, Message
    from src.discord.discord_webhook import send_discord_audit_log
    import datetime
    agente = Agente.query.filter_by(public_token=public_token).first()
    if not agente:
        return render_template('chat/public_chat.html', error='Agente não encontrado.'), 404
    send_discord_audit_log(
        action="Acesso público via token",
        user_id=None,
        object_type="Agente",
        object_id=agente.id,
        details=f"Token utilizado: {public_token} | IP: {request.remote_addr} | Horário: {datetime.datetime.utcnow().isoformat()} ",
        ip_address=request.remote_addr
    )
    messages = Message.query.filter_by(chat_id=agente.chats[0].id).order_by(Message.created_at.asc()).limit(20).all() if agente.chats else []
    return render_template('chat/public_chat.html', agente=agente, messages=messages, error=None) 

@chat_bp.route('/public-chat/<public_token>/send', methods=['POST'])
def public_chat_send(public_token):
    from src.models import Agente, Message, Chat
    from src.services.chat_service import ChatService
    from flask import request, jsonify
    import datetime
    agente = Agente.query.filter_by(public_token=public_token).first()
    if not agente:
        return jsonify({'error': 'Agente não encontrado.'}), 404
    data = request.get_json()
    user_message = data.get('message', '').strip()
    if not user_message:
        return jsonify({'error': 'Mensagem vazia.'}), 400
    chat = Chat.query.filter_by(agent_id=agente.id, user_id=None).first()
    if not chat:
        chat = Chat(agent_id=agente.id, user_id=None, title=f'Chat Público {agente.nome}', created_at=datetime.datetime.utcnow())
        from src.extensions import db
        db.session.add(chat)
        db.session.commit()
    from src.extensions import db
    msg_user = Message(chat_id=chat.id, is_user=True, message=user_message, created_at=datetime.datetime.utcnow())
    db.session.add(msg_user)
    db.session.commit()
    response, error = ChatService.generate_agent_response(agente, user_message, chat)
    if error:
        return jsonify({'error': error}), 500
    msg_bot = Message(chat_id=chat.id, is_user=False, message=response, created_at=datetime.datetime.utcnow())
    db.session.add(msg_bot)
    db.session.commit()
    return jsonify({'response': response}) 

@chat_bp.route('/chat-new')
@login_required
def chat_new():
    """Nova interface de chat usando TypeScript do ditto-ai"""
    if hasattr(current_user, 'role') and current_user.role == 'superadmin':
        agentes = Agente.query.order_by(Agente.nome).all()
    else:
        agentes = Agente.query.filter_by(user_id=current_user.id).all()
    
    agent_id = request.args.get('agent_id', type=int)
    agent = None
    if agent_id:
        if hasattr(current_user, 'role') and current_user.role == 'superadmin':
            agent = Agente.query.filter_by(id=agent_id).first()
        else:
            agent = Agente.query.filter_by(id=agent_id, user_id=current_user.id).first()
    
    if not agent and agentes:
        agent = agentes[0]
    
    chats = ChatService.get_user_chats()
    chat = None
    if agent and (not chats or not any(c.agent_id == agent.id for c in chats)):
        chat, error = ChatService.create_chat(agent.id)
        if error:
            flash('Erro ao criar chat: ' + error, 'danger')
    
    return render_template('chat/chat_new.html', agent=agent, agentes=agentes, chats=chats, chat=chat, current_chat=chat, current_agent=agent)

@chat_bp.route('/ditto-ai/<path:filename>')
@login_required
def serve_ditto_ai(filename):
    """Serve arquivos estáticos do ditto-ai"""
    ditto_ai_path = os.path.join(os.path.dirname(__file__), '..', '..', 'ditto-ai', 'dist')
    return send_from_directory(ditto_ai_path, filename)

@chat_bp.route('/ditto-ai/')
@login_required
def serve_ditto_ai_index():
    """Serve o index.html do ditto-ai"""
    ditto_ai_path = os.path.join(os.path.dirname(__file__), '..', '..', 'ditto-ai', 'dist')
    return send_from_directory(ditto_ai_path, 'index.html') 
