import asyncio
import websockets
import json
import ssl
import pyodbc
import requests
import aiohttp_cors
import random
from flask import Flask, request, jsonify
from datetime import datetime
from aiohttp import web
from urllib.parse import urlparse, parse_qs
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from config.connection import database_intranet


class MessageUtil:
    """Utilidades para el manejo de mensajes"""
    
    @staticmethod
    def format_timestamp(timestamp):
        """Convierte timestamp a formato legible"""
        try:
            return datetime.fromtimestamp(int(timestamp)).strftime('%H:%M:%S')
        except:
            return datetime.now().strftime('%H:%M:%S')
    
    @staticmethod
    def normalize_phone(phone):
        if not phone:
            return ''
        # Eliminar caracteres no numéricos
        digits = ''.join(filter(str.isdigit, phone))
        # Si es móvil argentino con formato 549 + 11 dígitos, elimina el 9 intermedio
        if digits.startswith('549') and len(digits) == 13:
            digits = '54' + digits[3:]  # elimina el '9'
        return digits
    
    @staticmethod
    def extraer_datos_mensaje(raw_data, operador_id, cliente_phone, desde_operador=False, cliente_id=None):
        """
        Extrae y estructura los datos de un mensaje para logging o guardado en base de datos.
        """
        try:
            # Si es string plano, intentar convertir a JSON
            if isinstance(raw_data, str):
                data = json.loads(raw_data)
            else:
                data = raw_data
        except:
            # Si no es JSON válido, tratamos como texto plano
            data = {
                "body": raw_data.strip() if isinstance(raw_data, str) else "",
                "from": operador_id if desde_operador else cliente_phone
            }

        direction = "out" if desde_operador else "in"
        from_number = data.get("from", operador_id if desde_operador else cliente_phone)
        body = data.get("body", "").strip()
        timestamp = data.get("timestamp", datetime.now().timestamp())
        tipo = data.get("type", "text")

        # Normalizamos el timestamp a datetime
        if isinstance(timestamp, str):
            try:
                timestamp = float(timestamp)
            except:
                timestamp = datetime.now().timestamp()
        
        timestamp_dt = datetime.fromtimestamp(float(timestamp))

        # Convertir "desconocido" a None para la BD
        operador_final = None if operador_id == "desconocido" else operador_id
        
        mensaje = {
            "cliente_phone": MessageUtil.normalize_phone(cliente_phone),
            "operador_id": operador_final,
            "from_number": from_number,
            "body": body,
            "direction": direction,
            "timestamp": timestamp_dt,
            "tipo": tipo,
            "cliente_id": cliente_id 
        }

        print("\n📝 Datos extraídos del mensaje:")
        for k, v in mensaje.items():
            print(f"   {k}: {v}")

        return mensaje


class MessageHistory:
    """Gestión del historial de mensajes para evitar duplicados"""
    
    def __init__(self, max_size=100):
        self.history = []
        self.max_size = max_size
    
    def add_message(self, message_id, message_type):
        """Agrega mensaje al historial para evitar duplicados"""
        self.history.append({"id": message_id, "type": message_type, "time": datetime.now()})
        # Mantener solo los últimos mensajes
        if len(self.history) > self.max_size:
            self.history = self.history[-self.max_size:]
    
    def is_duplicate(self, message_id):
        """Verifica si el mensaje ya fue procesado"""
        return any(msg["id"] == message_id for msg in self.history)


class DatabaseManager:
    """Gestiona las operaciones de base de datos"""
    
    @staticmethod
    def obtener_o_crear_conversacion(conn, cliente_phone, operador_id=None, cliente_id=None):
        """
        Busca una conversación activa que coincida por cliente_phone, operador_id y cliente_id.
        Si no existe, crea la conversación según las reglas.
        """
        cursor = conn.cursor()
        cliente_phone_norm = MessageUtil.normalize_phone(cliente_phone)

        print("🔍 Buscando/creando conversación:")
        print(f"   📱 cliente_phone_norm: '{cliente_phone_norm}'")
        print(f"   👤 operador_id (raw): {operador_id!r} (tipo: {type(operador_id)})")
        print(f"   🆔 cliente_id (raw): {cliente_id!r} (tipo: {type(cliente_id)})")

        # Normalizar operador desconocido
        if operador_id == "desconocido":
            operador_id = None

        # Buscar conversación existente que coincida en los 3 campos
        cursor.execute("""
            SELECT TOP 1 id, cliente_id, operador_id
            FROM alaweb.dbo.usr_conversaciones
            WHERE cliente_phone = ?
              AND fecha_fin IS NULL
              AND (
                  cliente_id = ?
                  OR (? IS NULL AND cliente_id IS NULL)
              )
              AND (
                  operador_id = ?
                  OR (? IS NULL AND operador_id IS NULL)
              )
            ORDER BY fecha_inicio DESC
        """, (
            cliente_phone_norm,
            cliente_id, cliente_id,
            operador_id, operador_id
        ))

        row = cursor.fetchone()
        if row:
            conversacion_id, existing_cliente_id, existing_operador_id = row
            print(f"✅ Conversación encontrada: ID {conversacion_id}")
            print(f"   cliente_id en BD: {existing_cliente_id!r}, operador_id en BD: {existing_operador_id!r}")

            # Si llega cliente_id y la conversación existente no lo tiene → actualizar
            if cliente_id is not None and (existing_cliente_id is None or str(existing_cliente_id).strip() == ""):
                cursor.execute("""
                    UPDATE alaweb.dbo.usr_conversaciones
                    SET cliente_id = ?
                    WHERE id = ?
                """, (cliente_id, conversacion_id))
                conn.commit()
                print(f"🔄 Conversación {conversacion_id} actualizada con cliente_id {cliente_id}")

            return conversacion_id

        # No existe -> crear nueva según casos
        print("➕ No se encontró conversación exacta. Creando nueva...")
        print(f"   📱 Teléfono: {cliente_phone_norm}")
        print(f"   🆔 Cliente ID: {cliente_id if cliente_id is not None else 'No asignado'}")
        print(f"   👤 Operador ID: {operador_id if operador_id is not None else 'No asignado'}")

        if operador_id is None and cliente_id is None:
            # Potencial cliente nuevo - sin operador ni cliente_id
            cursor.execute("""
                INSERT INTO alaweb.dbo.usr_conversaciones (cliente_phone, fecha_inicio)
                OUTPUT INSERTED.id
                VALUES (?, GETDATE())
            """, (cliente_phone_norm,))
        elif operador_id is None and cliente_id is not None:
            # Potencial con cliente_id (raro, pero cubrimos)
            cursor.execute("""
                INSERT INTO alaweb.dbo.usr_conversaciones (cliente_phone, fecha_inicio, cliente_id)
                OUTPUT INSERTED.id
                VALUES (?, GETDATE(), ?)
            """, (cliente_phone_norm, cliente_id))
        elif operador_id is not None and cliente_id is not None:
            # Cliente conocido con operador y cliente_id
            cursor.execute("""
                INSERT INTO alaweb.dbo.usr_conversaciones (cliente_phone, operador_id, fecha_inicio, cliente_id)
                OUTPUT INSERTED.id
                VALUES (?, ?, GETDATE(), ?)
            """, (cliente_phone_norm, operador_id, cliente_id))
        else:
            # operador_id dado, pero sin cliente_id
            cursor.execute("""
                INSERT INTO alaweb.dbo.usr_conversaciones (cliente_phone, operador_id, fecha_inicio)
                OUTPUT INSERTED.id
                VALUES (?, ?, GETDATE())
            """, (cliente_phone_norm, operador_id))

        new_row = cursor.fetchone()
        conversacion_id = new_row[0] if new_row else None
        conn.commit()

        print(f"✅ Nueva conversación creada: ID {conversacion_id}")
        return conversacion_id
    
    @staticmethod
    def guardar_mensaje_en_bd(conn, conversacion_id, from_number, body, direction, tipo, timestamp):
        """
        Guarda un mensaje en la base de datos.
        Evita duplicados verificando timestamp, dirección y remitente.
        """
        cursor = conn.cursor()
        
        # Verificar duplicados por timestamp, dirección y remitente (evitamos comparar TEXT)
        cursor.execute("""
            SELECT id FROM alaweb.dbo.usr_mensajes
            WHERE conversacion_id = ? 
            AND from_number = ?
            AND direction = ?
            AND ABS(DATEDIFF(SECOND, timestamp, ?)) < 2
        """, conversacion_id, from_number, direction, timestamp)
        
        existing_msg = cursor.fetchone()
        if existing_msg:
            print("⚠️ Mensaje duplicado detectado en BD (por timestamp y emisor) - no se guarda")
            return False
        
        # Insertar el mensaje
        try:
            cursor.execute("""
                INSERT INTO alaweb.dbo.usr_mensajes 
                (conversacion_id, from_number, body, direction, tipo, timestamp)
                VALUES (?, ?, ?, ?, ?, ?)
            """, conversacion_id, from_number, body, direction, tipo, timestamp)
            conn.commit()
            
            print(f"💾 Mensaje guardado en BD - Conversación: {conversacion_id}, Dirección: {direction}, Tipo: {tipo}")
            return True
        except Exception as e:
            print(f"❌ Error al insertar mensaje: {e}")
            conn.rollback()
            return False
    
    @staticmethod
    def asignar_operador_automatico(conn, conversacion_id):
        """Asigna automáticamente un operador a una conversación"""
        cursor = conn.cursor()
        
        # Verificar que la conversación aún no tenga operador
        cursor.execute("""
            SELECT operador_id FROM alaweb.dbo.usr_conversaciones
            WHERE id = ? AND operador_id IS NULL
        """, (conversacion_id,))
        
        row = cursor.fetchone()
        if not row:
            return None
            
        # Ejecutar asignación automática usando algoritmo de pesos
        hoy = datetime.now().strftime("%Y-%m-%d")
        fecha_inicio = f"{hoy} 00:00:00.000"
        fecha_fin = f"{hoy} 23:59:59.000"
        
        cursor.execute(f"""
            SELECT usu_id, COUNT(conv.id) as carga
            FROM intranet.dbo.usuarios usu
            LEFT JOIN alaweb.dbo.usr_conversaciones conv 
                ON usu.usu_id = conv.operador_id
                AND conv.fecha_inicio BETWEEN '{fecha_inicio}' AND '{fecha_fin}'
            WHERE usu.usu_asigna_conversacion = 1
            GROUP BY usu_id
        """)
        
        cargas = {row[0]: row[1] for row in cursor.fetchall()}

        if not cargas:
            return None
            
        max_carga = max(cargas.values()) + 1
        pesos = {op: max_carga - carga for op, carga in cargas.items()}
        operadores = list(pesos.keys())
        probabilidades = list(pesos.values())
        operador_id_asignado = random.choices(operadores, weights=probabilidades, k=1)[0]

        # Actualizar conversación con operador asignado
        cursor.execute("""
            UPDATE alaweb.dbo.usr_conversaciones SET operador_id = ? WHERE id = ?
        """, (operador_id_asignado, conversacion_id))
        conn.commit()
        
        print(f"✅ Conversación {conversacion_id} asignada automáticamente al operador {operador_id_asignado}")
        return str(operador_id_asignado)


class ConnectionManager:
    """Gestiona las conexiones de websockets"""
    
    def __init__(self):
        self.connected_clients = set()
        self.operador_connections = {}  # clave: operador_id_cliente_phone_cliente_id, valor: websocket
    
    def add_connection(self, operador_id, cliente_phone, cliente_id, websocket):
        """Agrega una nueva conexión"""
        cliente_phone_norm = MessageUtil.normalize_phone(cliente_phone)
        
        # Generar clave única
        clave = f"{operador_id}_{cliente_phone_norm}_{cliente_id}"
        print(f"🔑 Generando clave única: {clave}")
        
        # Verificar si ya existe una conexión para esta clave
        if clave in self.operador_connections:
            print(f"⚠️ Ya existe una conexión para Operador: {operador_id}, Cliente: {cliente_phone_norm}, ID: {cliente_id} - cerrando la anterior")
            try:
                return self.operador_connections[clave], clave
            except:
                pass
        
        self.operador_connections[clave] = websocket
        self.connected_clients.add(websocket)
        
        print(f"\n🔌 CONEXIÓN WEBSOCKET ESTABLECIDA - Operador: {operador_id}, Cliente: {cliente_phone_norm}, ClienteID: {cliente_id}")
        return websocket, clave
    
    def remove_connection(self, websocket, clave=None):
        """Elimina una conexión"""
        self.connected_clients.discard(websocket)
        
        if clave:
            self.operador_connections.pop(clave, None)
            print(f"🧹 Operador desconectado: {clave}")
            print(f"📊 Operadores restantes: {len(self.connected_clients)}")
        else:
            # Buscar clave por valor
            keys_to_remove = [k for k, v in self.operador_connections.items() if v == websocket]
            for key in keys_to_remove:
                self.operador_connections.pop(key, None)
                print(f"🧹 Operador desconectado: {key}")
            print(f"📊 Operadores restantes: {len(self.connected_clients)}")
    
    def find_connection_by_phone(self, cliente_phone):
        """Busca una conexión por el teléfono del cliente"""
        cliente_phone_norm = MessageUtil.normalize_phone(cliente_phone)
        
        for clave, ws in self.operador_connections.items():
            try:
                partes = clave.split('_')
                if len(partes) == 3 and partes[1] == cliente_phone_norm:
                    return ws, clave, partes[0], partes[2]
            except:
                continue
        
        return None, None, None, None


class WebSocketServer:
    """Servidor de WebSockets"""
    
    def __init__(self):
        self.conn_manager = ConnectionManager()
        self.message_history = MessageHistory(max_size=100)
        
    async def websocket_handler(self, websocket):
        """Manejador de conexiones websocket"""
        operador_id = None
        cliente_phone = None
        cliente_id = None
        clave = None

        # Intentar obtener parámetros del primer mensaje JSON
        try:
            first_message = await websocket.recv()
            params = json.loads(first_message)
            operador_id = params.get("operador_id")
            cliente_phone = params.get("cliente_phone")
            cliente_id = params.get("cliente_id")
        except websockets.exceptions.ConnectionClosedOK as e:
            print("ℹ️ Conexión cerrada limpiamente (cambio de cliente), sin mensaje inicial.")
            return
        except Exception as e:
            print(f"❌ Error obteniendo parámetros del primer mensaje: {e}")
            return

        # Validar parámetros
        if not operador_id or not cliente_phone:
            await websocket.send(json.dumps({
                "type": "error",
                "message": "Se requieren operador_id y cliente_phone para la conexión"
            }))
            await websocket.close()
            return

        # Registrar la conexión
        ws, clave = self.conn_manager.add_connection(operador_id, cliente_phone, cliente_id, websocket)
        
        # Normalizar teléfono
        cliente_phone_norm = MessageUtil.normalize_phone(cliente_phone)
        
        # Crear o verificar conversación inmediatamente al establecer la conexión
        try:
            conn = database_intranet()
            conversacion_id = DatabaseManager.obtener_o_crear_conversacion(
                conn,
                cliente_phone=cliente_phone_norm,
                operador_id=operador_id,
                cliente_id=cliente_id
            )
            if conversacion_id:
                print(f"🟢 CONVERSACIÓN VERIFICADA/CREADA AL CONECTAR - ID: {conversacion_id}, Operador: {operador_id}, Cliente: {cliente_phone_norm}, ClienteID: {cliente_id}")
            conn.close()
        except Exception as e:
            print(f"❌ ERROR AL CREAR/VERIFICAR CONVERSACIÓN: {str(e)}")
            if 'conn' in locals():
                conn.close()

        try:
            async for message in websocket:
                timestamp = MessageUtil.format_timestamp(datetime.now().timestamp())
                print(f"\n📨 MENSAJE DE OPERADOR [{timestamp}] - Clave: {clave}")
                print(f"💬 Contenido: {message}")

                message_id = f"op_{clave}_{datetime.now().timestamp()}"

                if self.message_history.is_duplicate(message_id):
                    print("⚠️ MENSAJE DUPLICADO - IGNORADO")
                    continue

                self.message_history.add_message(message_id, "operator")
                
                try:
                    data = json.loads(message)

                    # Detectar si es una solicitud para iniciar conversación
                    if data.get("tipo") == "iniciar_chat":
                        conn = database_intranet()
                        conversacion_id = DatabaseManager.obtener_o_crear_conversacion(
                            conn,
                            cliente_phone=cliente_phone_norm,
                            operador_id=operador_id,
                            cliente_id=cliente_id
                        )
                        conn.close()
                        print(f"🟢 CONVERSACIÓN REGISTRADA - Operador: {operador_id}, Cliente: {cliente_phone_norm}")
                        continue  # No procesar como mensaje, solo registrar la conversación

                    # Si no es iniciar_chat, es un mensaje normal
                    msg_info = MessageUtil.extraer_datos_mensaje(
                        data, 
                        operador_id, 
                        cliente_phone_norm, 
                        desde_operador=True, 
                        cliente_id=cliente_id
                    )

                    cliente_id = msg_info.get("cliente_id")

                    # GUARDAR EN BASE DE DATOS
                    try:
                        conn = database_intranet()
                        
                        conversacion_id = DatabaseManager.obtener_o_crear_conversacion(
                            conn,
                            cliente_phone=msg_info["cliente_phone"],
                            operador_id=msg_info["operador_id"],
                            cliente_id=msg_info.get("cliente_id")
                        )
                    
                        DatabaseManager.guardar_mensaje_en_bd(
                            conn,
                            conversacion_id=conversacion_id,
                            from_number=msg_info["from_number"],
                            body=msg_info["body"],
                            direction=msg_info["direction"],
                            tipo=msg_info["tipo"],
                            timestamp=msg_info["timestamp"]
                        )
                    
                        conn.close()
                        print("✅ Mensaje del operador guardado correctamente")
                    except Exception as e:
                        print(f"❌ Error al guardar mensaje WebSocket en la BD: {e}")
                        import traceback
                        traceback.print_exc()
                    
                    phone = data.get('from', cliente_phone_norm)
                    body = data.get('body', '').strip()
                    print("📋 Formato: JSON")
                except json.JSONDecodeError:
                    phone = cliente_phone_norm
                    body = message.strip()
                    print("📋 Formato: Texto plano")

                print(f"📱 Enviando a: {phone}")
                print(f"💬 Mensaje final: {body}")

        except websockets.exceptions.ConnectionClosed:
            print(f"\n🔌 DESCONEXIÓN WEBSOCKET ({clave})")
        except Exception as e:
            print(f"\n❌ ERROR EN WEBSOCKET ({clave}): {e}")
            import traceback
            traceback.print_exc()
        finally:
            self.conn_manager.remove_connection(websocket, clave)

# Mensajes Clientes
class HTTPServer:
    """Servidor HTTP"""
    
    def __init__(self, conn_manager, message_history):
        self.conn_manager = conn_manager
        self.message_history = message_history
    
    async def handle_post(self, request):
        """Manejador de peticiones HTTP POST"""
        # 🎯 AQUÍ LLEGAN LOS MENSAJES DEL CLIENTE
        try:
            data = await request.json()
            timestamp = MessageUtil.format_timestamp(data.get('timestamp', datetime.now().timestamp()))
            
            message_id = f"msg_{data.get('from')}_{data.get('timestamp')}"
            is_operator_message = data.get('procesado', '').upper() == 'SI'
            message_type = "RESPUESTA OPERADOR" if is_operator_message else "MENSAJE CLIENTE"
            
            print(f"\n📨 {message_type} [{timestamp}]")
            print(f"   📱 Teléfono: {data.get('from')}")
            print(f"   💬 Mensaje: {data.get('body')}")

            # Evitar duplicados
            if self.message_history.is_duplicate(message_id):
                print(f"   ⚠️  MENSAJE DUPLICADO - IGNORADO")
                return web.Response(text="Mensaje duplicado ignorado")

            self.message_history.add_message(message_id, "client" if not is_operator_message else "operator_response")

            # Normalizar teléfono del cliente
            cliente_phone = MessageUtil.normalize_phone(data.get("from"))

            operador_id = "desconocido"
            clave_encontrada = None

            # Verificar si hay un operador conectado para este cliente
            print(f"\n🔍 Buscando operador para cliente: {cliente_phone}")
            print(f"📱 Conexiones activas: {list(self.conn_manager.operador_connections.keys())}")
            
            operador_ws, clave_encontrada, operador_id, cliente_id = self.conn_manager.find_connection_by_phone(cliente_phone)
            
            if not operador_id:
                operador_id = "desconocido"
                print("❌ No se encontró operador conectado para este cliente")

            # Extraer datos del mensaje
            msg_info = MessageUtil.extraer_datos_mensaje(
                raw_data=data,
                operador_id=operador_id,
                cliente_phone=cliente_phone,
                desde_operador=False,
                cliente_id=cliente_id if 'cliente_id' in locals() else None
            )

            print(f"\n📝 Datos extraídos del mensaje del CLIENTE (operador asignado: {operador_id}):")
            for k, v in msg_info.items():
                print(f"   {k}: {v}")

            # Guardar mensaje en BD y obtener o crear conversación
            conversacion_id = None
            try:
                conn = database_intranet()
                # Decidir cómo crear la conversación basado en el contexto
                operador_final = None if msg_info["operador_id"] == "desconocido" else msg_info["operador_id"]
                cliente_final = cliente_id if 'cliente_id' in locals() and cliente_id else None
                
                print(f"\n📝 Creando/buscando conversación con:")
                print(f"   📱 Teléfono: {msg_info['cliente_phone']}")
                print(f"   👤 Operador: {operador_final if operador_final else 'NULL'}")
                print(f"   🆔 Cliente ID: {cliente_final if cliente_final else 'NULL'}")
                
                conversacion_id = DatabaseManager.obtener_o_crear_conversacion(
                    conn,
                    cliente_phone=msg_info["cliente_phone"],
                    operador_id=operador_final,
                    cliente_id=cliente_final
                )

                DatabaseManager.guardar_mensaje_en_bd(
                    conn,
                    conversacion_id=conversacion_id,
                    from_number=msg_info["from_number"],
                    body=msg_info["body"],
                    direction=msg_info["direction"],
                    tipo=msg_info["tipo"],
                    timestamp=msg_info["timestamp"]
                )

                # Si operador desconocido, hacer asignación automática
                if msg_info["operador_id"] == "desconocido":
                    operador_id = DatabaseManager.asignar_operador_automatico(conn, conversacion_id)

                conn.close()
                print("✅ Mensaje del cliente guardado correctamente")
            except Exception as e:
                print(f"❌ Error al guardar mensaje POST en la BD o asignar operador: {e}")
                import traceback
                traceback.print_exc()

            # Distribuir mensaje al operador conectado si existe
            operador_ws, clave_encontrada, _, _ = self.conn_manager.find_connection_by_phone(cliente_phone)
            
            if not operador_ws:
                print(f"❌ No hay operador conectado para cliente {cliente_phone}")
                return web.Response(text=f"Operador no conectado para cliente {cliente_phone}", status=503)

            try:
                operador_ws.last_client_phone = cliente_phone
                await operador_ws.send(json.dumps(data))
                print(f"✅ Mensaje reenviado a operador para cliente {cliente_phone} (clave: {clave_encontrada})")
                return web.Response(text="Mensaje entregado al operador")
            except websockets.exceptions.ConnectionClosed:
                print(f"❌ WebSocket cerrado para {clave_encontrada}")
                self.conn_manager.remove_connection(operador_ws, clave_encontrada)
                return web.Response(status=503, text="WebSocket cerrado")
            except Exception as e:
                print(f"❌ Error enviando al operador: {e}")
                return web.Response(status=500, text="Error al reenviar al operador")

        except Exception as e:
            print(f"\n❌ ERROR EN HTTP POST: {e}")
            import traceback
            traceback.print_exc()
            return web.Response(status=500, text=str(e))
        finally:
            print("=" * 50)
            
    async def handle_get_status(self, request):
        """Manejador de peticiones HTTP GET para estado del servidor"""
        return web.json_response({
            "status": "ok",
            "message": "Servidor WebSocket activo",
            "timestamp": datetime.now().isoformat(),
            "websocket_url": "wss://notificador.divisiongps.com.ar:6789",
            "operadores_conectados": len(self.conn_manager.operador_connections),
            "clientes_conectados": len(self.conn_manager.connected_clients)
        })


class ServerApp:
    """Aplicación principal del servidor"""
    
    def __init__(self):
        self.conn_manager = ConnectionManager()
        self.message_history = MessageHistory()
        self.ws_server = WebSocketServer()
        self.http_server = HTTPServer(self.conn_manager, self.message_history)
        
        # Compartir los mismos managers
        self.ws_server.conn_manager = self.conn_manager
        self.ws_server.message_history = self.message_history
    
    async def start_servers(self):
        """Inicia los servidores en modo producción (con SSL)"""
        print("\n🚀 INICIANDO SERVIDORES...")

        # Crear la aplicación HTTP
        app = web.Application()

        # CONFIGURAR CORS
        cors = aiohttp_cors.setup(app, defaults={
            "*": aiohttp_cors.ResourceOptions(
                allow_credentials=True,
                expose_headers="*",
                allow_headers="*",
                allow_methods="*"
            )
        })

        # Agregar rutas CON CORS
        post_route = app.router.add_post('/emitir', self.http_server.handle_post)
        cors.add(post_route)
        
        # Agregar GET para verificar estado
        get_route = app.router.add_get('/', self.http_server.handle_get_status)
        cors.add(get_route)

        # WebSocket seguro para producción
        ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
        ssl_context.load_cert_chain(
            certfile="/home/stopcar/NotificadorGo/config/certificados/fullchain.pem",
            keyfile="/home/stopcar/NotificadorGo/config/certificados/privkey.pem"
        )

        # WebSocket Server (puerto 6789)
        ws_server = await websockets.serve(
            self.ws_server.websocket_handler, 
            "notificador.divisiongps.com.ar", 
            6789, 
            ssl=ssl_context
        )
        print("🔐 WebSocket seguro: wss://notificador.divisiongps.com.ar:6789")

        # Servidor HTTPS (puerto 8081)
        runner = web.AppRunner(app)
        await runner.setup()
        site = web.TCPSite(runner, 'notificador.divisiongps.com.ar', 8081, ssl_context=ssl_context)
        await site.start()
        print("🌐 HTTPS: https://notificador.divisiongps.com.ar:8081")

        # RESUMEN DE CONFIGURACIÓN
        print("\n📋 CONFIGURACIÓN DE PUERTOS:")
        print("   🔐 WebSocket SSL: wss://notificador.divisiongps.com.ar:6789")
        print("   🌐 HTTPS API: https://notificador.divisiongps.com.ar:8081")
        print("   🔧 CORS: Habilitado para todos los orígenes")
        print("\n📡 ENDPOINTS DISPONIBLES:")
        print("   POST /emitir - Recibir mensajes de clientes")
        print("   GET  /      - Estado del servidor (puerto 8081)")
        print("   WebSocket   - Conexión en tiempo real (puerto 6789)")

        print("\n✅ SERVIDORES ACTIVOS - Esperando conexiones...")
        print("=" * 50)

        try:
            while True:
                await asyncio.sleep(3600)
        except KeyboardInterrupt:
            print("\n🛑 CERRANDO SERVIDORES...")
            ws_server.close()
            await ws_server.wait_closed()
            await runner.cleanup()
            print("✅ Servidores cerrados")
    
    async def start_servers_local(self):
        """Inicia los servidores en modo desarrollo (sin SSL)"""
        print("\n🚀 INICIANDO SERVIDORES (MODO INSEGURO)...")

        # Crear la aplicación HTTP
        app = web.Application()

        # CONFIGURAR CORS
        cors = aiohttp_cors.setup(app, defaults={
            "*": aiohttp_cors.ResourceOptions(
                allow_credentials=True,
                expose_headers="*",
                allow_headers="*",
                allow_methods="*"
            )
        })

        # Agregar rutas CON CORS
        post_route = app.router.add_post('/emitir', self.http_server.handle_post)
        cors.add(post_route)
        
        # Agregar GET para verificar estado
        get_route = app.router.add_get('/', self.http_server.handle_get_status)
        cors.add(get_route)

        # WebSocket Server sin SSL (puerto 6789)
        ws_server = await websockets.serve(
            self.ws_server.websocket_handler, 
            "0.0.0.0",  # Escucha en todas las interfaces
            6789
        )
        print("💬 WebSocket: ws://0.0.0.0:6789")

        # Servidor HTTP (puerto 8081)
        runner = web.AppRunner(app)
        await runner.setup()
        site = web.TCPSite(runner, '0.0.0.0', 8081)
        await site.start()
        print("🌐 HTTP: http://0.0.0.0:8081")

        # RESUMEN DE CONFIGURACIÓN
        print("\n📋 CONFIGURACIÓN DE PUERTOS:")
        print("   💬 WebSocket: ws://0.0.0.0:6789")
        print("   🌐 HTTP API:  http://0.0.0.0:8081")
        print("   🔧 CORS: Habilitado para todos los orígenes")
        print("\n📡 ENDPOINTS DISPONIBLES:")
        print("   POST /emitir - Recibir mensajes de clientes")
        print("   GET  /      - Estado del servidor (puerto 8081)")
        print("   WebSocket   - Conexión en tiempo real (puerto 6789)")

        print("\n✅ SERVIDORES ACTIVOS (SIN SSL) - Esperando conexiones...")
        print("=" * 50)

        try:
            while True:
                await asyncio.sleep(3600)
        except KeyboardInterrupt:
            print("\n🛑 CERRANDO SERVIDORES...")
            ws_server.close()
            await ws_server.wait_closed()
            await runner.cleanup()
            print("✅ Servidores cerrados (modo inseguro)")


if __name__ == "__main__":
    # Crear e iniciar la aplicación del servidor
    app = ServerApp()
    
    # Descomentar según corresponda:
    asyncio.run(app.start_servers_local())  # Para desarrollo local
    # asyncio.run(app.start_servers())        # Para producción con SSL