from flask import Flask
from tts_utils import TTSUtils
from inference import InferenceManager
from huggingface_utils import HuggingFaceUtils
from flow_bot import FlowBot
from tunnel_manager import TunnelManager
from routes import Routes
from session_manager import SessionManager
from data_manager import DataManager
import yaml
import os
import webbrowser
import threading
import time
import sys
from datetime import timedelta
import logging
from colorama import init, Fore, Back, Style

# Inicializar colorama para Windows
init()

def print_startup_checklist():
    """Muestra el checklist de inicio con colores"""
    print(f"\n{Fore.CYAN}{'='*50}")
    print(f"{Fore.YELLOW}🤖 Iniciando Asistente Virtual...")
    print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}\n")

    # Checklist de Modelos
    print(f"{Fore.MAGENTA}📋 Modelos de IA:{Style.RESET_ALL}")
    print(f"{Fore.GREEN}✓ {Fore.WHITE}Principal: {Fore.YELLOW}Gemini 8b 🧠")
    print(f"{Fore.GREEN}✓ {Fore.WHITE}Respaldo: {Fore.YELLOW}Mixtral 7b ⚡\n")

    # Checklist de TTS
    print(f"{Fore.MAGENTA}🎤 Motores TTS:{Style.RESET_ALL}")
    print(f"{Fore.GREEN}✓ {Fore.WHITE}EDGE (Principal)")
    print(f"  {Fore.CYAN}└─ Voz: Jorge (MX) 📢")
    print(f"{Fore.GREEN}✓ {Fore.WHITE}EDGE_ES")
    print(f"  {Fore.CYAN}└─ Voz: Álvaro (ES) 🌐")
    print(f"{Fore.GREEN}✓ {Fore.WHITE}VITS")
    print(f"  {Fore.CYAN}└─ Modelo Local 🔊\n")

    # Checklist de Modos
    print(f"{Fore.MAGENTA}📋 Modos Disponibles:{Style.RESET_ALL}")
    print(f"{Fore.GREEN}✓ {Fore.WHITE}Créditos 💰")
    print(f"{Fore.GREEN}✓ {Fore.WHITE}Seguros 🛡️")
    print(f"{Fore.GREEN}✓ {Fore.WHITE}Cobranza 💵\n")

    # Estado del Sistema
    print(f"{Fore.MAGENTA}🔄 Estado del Sistema:{Style.RESET_ALL}")

class WebChatbotApp:
    def __init__(self):
        # Mostrar checklist inicial
        print_startup_checklist()
        
        self.flask_app = Flask(__name__)
        self.flask_app.config['SECRET_KEY'] = os.urandom(24)
        self.flask_app.config['SESSION_TYPE'] = 'filesystem'
        self.flask_app.config['SESSION_FILE_DIR'] = './flask_session'
        self.flask_app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=2)
        self.tunnel_url = None
        
        self.load_config()
        self.init_components()
        self.routes = Routes(self.flask_app, self)
        
        # Inicializar DataManager
        self.data_manager = DataManager()
        
        print(f"{Fore.GREEN}✓ {Fore.WHITE}Aplicación inicializada correctamente{Style.RESET_ALL}")

    def load_config(self):
        try:
            config_path = os.path.join(os.path.dirname(__file__), 'config.yaml')
            with open(config_path, 'r', encoding='utf-8') as f:
                self.config = yaml.safe_load(f)
            print(f"{Fore.GREEN}✓ {Fore.WHITE}Configuración cargada")
        except Exception as e:
            print(f"{Fore.RED}✗ Error cargando config.yaml: {e}{Style.RESET_ALL}")
            self.config = {}
            
    def init_components(self):
        try:
            print(f"{Fore.CYAN}⚙️ Iniciando componentes...{Style.RESET_ALL}")
            
            elevenlabs_key = self.config.get('ELEVENLABS_API_KEY', '')
            huggingface_token = self.config.get('api_keys', {}).get('HUGGINGFACE_TOKEN', '')
            
            self.tts = TTSUtils(
                model_name='EDGE',
                elevenlabs_api_key=elevenlabs_key
            )
            self.inference = InferenceManager(self.config)
            self.hf_utils = HuggingFaceUtils(huggingface_token)
            self.flow_bot = FlowBot()
            self.session_manager = SessionManager()
            
            print(f"{Fore.GREEN}✓ {Fore.WHITE}Componentes iniciados correctamente{Style.RESET_ALL}")
            
        except Exception as e:
            print(f"{Fore.RED}✗ Error iniciando componentes: {e}{Style.RESET_ALL}")
            raise

    def reinit_components(self):
        try:
            self.load_config()
            
            elevenlabs_key = self.config.get('ELEVENLABS_API_KEY', '')
            huggingface_token = self.config.get('api_keys', {}).get('HUGGINGFACE_TOKEN', '')
            
            print(f"{Fore.CYAN}⚙️ Reiniciando componentes...{Style.RESET_ALL}")
            
            self.tts = TTSUtils(
                model_name='EDGE',
                elevenlabs_api_key=elevenlabs_key
            )
            self.inference = InferenceManager(self.config)
            self.hf_utils = HuggingFaceUtils(huggingface_token)
            
            print(f"{Fore.GREEN}✓ {Fore.WHITE}Componentes reiniciados correctamente{Style.RESET_ALL}")
            
        except Exception as e:
            print(f"{Fore.RED}✗ Error reiniciando componentes: {e}{Style.RESET_ALL}")
            raise

    def procesar_mensaje(self, mensaje):
        """Procesa un mensaje de texto y retorna la respuesta"""
        try:
            # Obtener el modo actual de la sesión
            session_id = self.session_manager.get_session()
            current_mode = self.session_manager.get_session_data(session_id, 'mode')
            
            # Verificar si estamos en proceso de recolección de datos
            if self.data_manager.is_collecting_data():
                respuesta = self.data_manager.handle_data_collection(
                    mensaje,
                    current_mode,
                    self.flow_bot.get_data_collection_steps
                )
                if respuesta:
                    return respuesta
            
            # Obtener contexto del flow_bot usando el modo actual
            contexto = self.flow_bot.get_context(current_mode, mensaje)
            
            # Obtener respuesta usando el inference manager
            respuesta = self.inference.get_response(
                prompt=mensaje,
                context=contexto
            )

            # Verificar que la respuesta no sea un mensaje de error o predeterminado
            mensajes_error = [
                "No se proporcionó un mensaje",
                "Lo siento, hubo un error",
                "Error de autenticación",
                "El servicio está ocupado",
                "No se pudo generar una respuesta coherente",
                "¡Hola! Soy tu asistente virtual"
            ]
            
            if not respuesta or any(msg in respuesta for msg in mensajes_error):
                print("Respuesta no válida del modelo, usando contexto directo")
                respuesta = self.flow_bot.get_success_message(current_mode)
                # Iniciar recolección de datos si es necesario
                if "nombre" in respuesta.lower():
                    self.data_manager.start_data_collection()
            
            return respuesta
            
        except Exception as e:
            print(f"Error procesando mensaje: {e}")
            return self.flow_bot.get_negative_response(current_mode)

    def procesar_audio(self, audio_path):
        """Procesa un archivo de audio y retorna mensaje y audio de respuesta"""
        try:
            # Transcribir audio a texto
            mensaje = self.inference.transcribe_audio(audio_path)
            print(f"Audio transcrito: {mensaje}")
            
            # Obtener respuesta
            respuesta = self.procesar_mensaje(mensaje)
            print(f"Respuesta generada: {respuesta}")
            
            # Convertir respuesta a audio
            audio_path = self.tts.text_to_speech(respuesta)
            return mensaje, audio_path
            
        except Exception as e:
            print(f"Error procesando audio: {e}")
            return "Error al procesar el audio", None

    def open_browser(self, url):
        time.sleep(2)
        webbrowser.open(url)

    def run(self, host='127.0.0.1', port=5000, debug=False, use_tunnel=True):
        try:
            if use_tunnel:
                try:
                    tunnel_manager = TunnelManager()
                    # Primero intentar obtener un túnel activo
                    self.tunnel_url = tunnel_manager.get_active_tunnel()
                    
                    if not self.tunnel_url:
                        # Si no hay túnel activo, limpiar y crear uno nuevo
                        tunnel_manager.setup_ngrok()
                        tunnel_manager.cleanup()
                        self.tunnel_url = tunnel_manager.start(port)
                    
                    if self.tunnel_url:
                        print(f"{Fore.GREEN}✓ {Fore.WHITE}Túnel iniciado en: {Fore.CYAN}{self.tunnel_url}{Style.RESET_ALL}")
                        threading.Thread(target=self.open_browser, args=(self.tunnel_url,)).start()
                    else:
                        print(f"{Fore.YELLOW}⚠️ No se pudo iniciar el túnel, continuando en modo local{Style.RESET_ALL}")
                except Exception as tunnel_error:
                    print(f"{Fore.YELLOW}⚠️ Continuando en modo local{Style.RESET_ALL}")
            
            print(f"\n{Fore.GREEN}✓ {Fore.WHITE}Servidor iniciado en: {Fore.CYAN}http://{host}:{port}{Style.RESET_ALL}\n")
            self.flask_app.run(host=host, port=port, debug=debug, threaded=True)
            
        except Exception as e:
            print(f"{Fore.RED}✗ Error iniciando el servidor: {e}{Style.RESET_ALL}")
            raise

    @property
    def app(self):
        return self.flask_app

if __name__ == '__main__':
    import os
    import psutil
    import socket
    
    def is_port_in_use(port):
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            return s.connect_ex(('localhost', port)) == 0
            
    def kill_process_on_port(port):
        for proc in psutil.process_iter(['pid', 'name', 'connections']):
            try:
                for conn in proc.connections():
                    if conn.laddr.port == port:
                        print(f"Terminando proceso anterior en puerto {port}")
                        proc.terminate()
                        proc.wait()
                        return True
            except (psutil.NoSuchProcess, psutil.AccessDenied):
                pass
        return False
    
    try:
        port = 5000
        if is_port_in_use(port):
            print(f"Puerto {port} en uso. Intentando liberar...")
            if kill_process_on_port(port):
                print("Proceso anterior terminado")
            else:
                print(f"No se pudo liberar el puerto {port}. Por favor, cierre la aplicación anterior manualmente.")
                exit(1)
        
        print("Iniciando nueva instancia de la aplicación...")
        app = WebChatbotApp()
        app.run()
        
    except Exception as e:
        print(f"Error al iniciar la aplicación: {e}")
        exit(1)