import serial
import serial.tools.list_ports
import subprocess
import time
import os
import requests
import json
import re

BAUDIOS = 9600
NOMBRE_DRIVER = "Prolific"  # filtro para el adaptador USB-RS232

# Configuración del servidor
SERVIDOR_URL = "http://isracolor.ddns.net/api/central/evento"  # Laravel dev server
REVISION_ID = 1  # Cambiar por el ID de la revisión actual
IDLE_TIMEOUT_SECONDS = 2.0  # tiempo de inactividad para considerar evento completo

# Archivo de log para eventos enviados
LOG_FILE = os.path.join(os.path.dirname(__file__), "eventos_enviados.log")

# Buffer para acumular líneas de un evento completo
evento_buffer = []
ultimo_evento_id = None

def reiniciar_dispositivo():
    """Reinicia el adaptador USB-Serial usando PnPUtil con desinstalación completa de drivers"""
    print("🔄 Reiniciando dispositivo con PnPUtil...")
    try:
        # Paso 1: Desinstalar dispositivo con driver (equivalente a marcar "Se intentó quitar el controlador")
        print("📤 Desinstalando dispositivo PL2303 con driver...")
        subprocess.run(["pnputil", "/remove-device", "/force", "*PL2303*"], capture_output=True)
        time.sleep(3)
        
        # Paso 2: Desinstalar dispositivo con error específico
        print("📤 Desinstalando dispositivo con error 'This is not prolific'...")
        subprocess.run(["pnputil", "/remove-device", "/force", "*This is not prolific*"], capture_output=True)
        time.sleep(3)
        
        # Paso 3: Desinstalar por ID de dispositivo USB Prolific
        print("📤 Desinstalando por ID de dispositivo USB Prolific...")
        subprocess.run(["pnputil", "/remove-device", "/force", "*USB*VID_067B*"], capture_output=True)
        time.sleep(3)
        
        # Paso 4: Desinstalar drivers específicos de Prolific
        print("📤 Desinstalando drivers de Prolific...")
        subprocess.run(["pnputil", "/delete-driver", "ser2pl64.inf", "/uninstall"], capture_output=True)
        time.sleep(3)
        
        # Paso 5: Limpiar drivers obsoletos
        print("🧹 Limpiando drivers obsoletos...")
        subprocess.run(["pnputil", "/delete-driver", "ser2pl64.inf", "/force"], capture_output=True)
        time.sleep(3)
        
        # Paso 6: Rescaneo inicial
        print("🔍 Rescaneando dispositivos...")
        subprocess.run(["pnputil", "/scan-devices"], capture_output=True)
        time.sleep(5)
        
        # Paso 7: Forzar instalación del driver correcto
        forzar_driver_correcto()
        
        # Paso 8: Rescan final
        print("🔍 Rescan final...")
        subprocess.run(["pnputil", "/scan-devices"], capture_output=True)
        time.sleep(5)
        
        print("✅ Reinicio completado. El dispositivo debería aparecer como 'Prolific USB-to-Serial Comm Port'.")
        print("💡 Si el problema persiste, desconecta y reconecta físicamente el cable USB.")
        
    except Exception as e:
        print(f"⚠️ Error al ejecutar PnPUtil: {e}")

def forzar_driver_correcto():
    """Fuerza la instalación del driver correcto de Prolific"""
    print("🔧 Forzando instalación del driver correcto...")
    try:
        # Usar devcon para forzar la instalación del driver correcto
        driver_inf = r"C:\WINDOWS\system32\DRIVERS\ser2pl64.inf"
        if os.path.exists(driver_inf):
            print("📦 Instalando driver ser2pl64.inf...")
            subprocess.run(["pnputil", "/add-driver", driver_inf, "/install"], capture_output=True)
            time.sleep(3)
        
        # También intentar con el driver .sys
        driver_sys = r"C:\WINDOWS\system32\DRIVERS\ser2pl64.sys"
        if os.path.exists(driver_sys):
            print("📦 Instalando driver ser2pl64.sys...")
            subprocess.run(["pnputil", "/add-driver", driver_sys, "/install"], capture_output=True)
            time.sleep(3)
            
        print("✅ Driver forzado instalado.")
        
    except Exception as e:
        print(f"⚠️ Error al forzar driver: {e}")

def extraer_id_evento(linea):
    """Extrae el ID del evento de una línea (formato: 00511: sab 30-ago-25 22:13:57: ...)"""
    match = re.match(r'^(\d+):', linea)
    if match:
        return match.group(1)
    return None

def es_inicio_evento(linea):
    """Determina si una línea es el inicio de un nuevo evento"""
    # Patrón para identificar inicio de evento: número seguido de dos puntos
    return bool(re.match(r'^\d+:\s*[a-z]{3}\s+\d{2}-[a-z]{3}-\d{2}', linea))

def registrar_evento_en_log(evento_raw, exito, detalle=""):
    """Escribe en el log el evento enviado, resultado y detalles."""
    try:
        timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
        primera_linea = evento_raw.split('\n', 1)[0] if evento_raw else ""
        evento_id = extraer_id_evento(primera_linea) or "N/A"
        with open(LOG_FILE, "a", encoding="utf-8") as f:
            f.write(f"[{timestamp}] ID {evento_id} - {'OK' if exito else 'FAIL'}{(' - ' + detalle) if detalle else ''}\n")
            f.write(evento_raw + "\n")
            f.write("-" * 80 + "\n")
    except Exception as e:
        print(f"⚠️ No se pudo escribir en el log: {e}")

def procesar_evento_completo(evento_completo):
    """Procesa y envía un evento completo al servidor"""
    global ultimo_evento_id
    
    # Extraer ID del evento
    primera_linea = evento_completo[0] if evento_completo else ""
    evento_id = extraer_id_evento(primera_linea)
    
    # Evitar enviar el mismo evento múltiples veces
    if evento_id == ultimo_evento_id:
        print(f"⏭️ Evento {evento_id} ya procesado, saltando...")
        return False
    
    ultimo_evento_id = evento_id
    
    # Unir todas las líneas del evento
    evento_raw = '\n'.join(evento_completo)
    
    print(f"📡 EVENTO #{evento_id} ──────────────────────────────────────────────────")
    print(evento_raw)
    print("─" * 65)
    
    # Enviar al servidor
    return enviar_evento_servidor(evento_raw)

def enviar_evento_servidor(evento_raw):
    """Envía el evento al servidor web"""
    exito = False
    detalle = ""
    try:
        datos = {
            'evento': evento_raw,
            'revision_id': REVISION_ID
        }

        response = requests.post(
            SERVIDOR_URL,
            json=datos,
            timeout=5,
            headers={'Content-Type': 'application/json'}
        )

        if response.status_code == 200:
            try:
                resultado = response.json()
            except Exception:
                print("❌ Respuesta no JSON del servidor")
                detalle = "200/no-json"
            else:
                if resultado.get('success'):
                    print(f"✅ Evento enviado correctamente al servidor")
                    exito = True
                    detalle = "200/success"
                else:
                    mensaje = resultado.get('message', 'Error desconocido')
                    print(f"❌ Error del servidor: {mensaje}")
                    detalle = f"200/error:{mensaje}"
        else:
            print(f"❌ Error HTTP {response.status_code}: {response.text}")
            detalle = f"HTTP {response.status_code}"

    except requests.exceptions.ConnectionError:
        print(f"❌ No se pudo conectar al servidor: {SERVIDOR_URL}")
        print(f"💡 Verifica que el servidor Laravel esté ejecutándose en: {SERVIDOR_URL}")
        detalle = "ConnectionError"
    except requests.exceptions.Timeout:
        print(f"❌ Timeout al enviar evento al servidor")
        detalle = "Timeout"
    except Exception as e:
        print(f"❌ Error inesperado al enviar evento: {e}")
        detalle = f"Exception: {e}"
    finally:
        registrar_evento_en_log(evento_raw, exito, detalle)

    return exito

def detectar_puerto():
    """Detecta automáticamente el puerto COM del adaptador"""
    puertos = list(serial.tools.list_ports.comports())
    if not puertos:
        print("❌ No se detectaron puertos COM.")
        return None

    print("🔎 Puertos detectados:")
    for p in puertos:
        print(f" - {p.device} ({p.description})")

    # Verifica si hay dispositivos con error "This is not prolific"
    for p in puertos:
        if "THIS IS NOT PROLIFIC" in p.description.upper():
            print("🚨 Detectado dispositivo con error 'This is not prolific'")
            print("🔄 Iniciando reinicio automático del dispositivo...")
            reiniciar_dispositivo()
            time.sleep(5)  # Espera a que se complete el reinicio
            return None  # Retorna None para que reintente la detección

    # Busca específicamente el nombre correcto del driver Prolific
    for p in puertos:
        if "PROLIFIC USB-TO-SERIAL COMM PORT" in p.description.upper():
            print(f"✅ Dispositivo Prolific correcto detectado: {p.device}")
            return p.device
    
    # Busca un puerto con "USB" o "Prolific" en la descripción (sin error)
    for p in puertos:
        if ("USB" in p.description.upper() or "PROLIFIC" in p.description.upper()) and "NOT PROLIFIC" not in p.description.upper():
            print(f"✅ Usando {p.device}")
            return p.device

    # Si no encuentra nada especial, usa el primero (que no tenga error)
    for p in puertos:
        if "NOT PROLIFIC" not in p.description.upper():
            print(f"✅ Usando {p.device}")
            return p.device
    
    return None

def iniciar_listener():
    """Inicia el listener en bucle, reintentando si hay errores"""
    global evento_buffer
    
    print("🚀 INICIANDO LISTENER PARA CENTRAL ID3000")
    print("=" * 60)
    print(f"🌐 Servidor: {SERVIDOR_URL}")
    print(f"📋 Revisión ID: {REVISION_ID}")
    print(f"⚙️ Baudios: {BAUDIOS}")
    print("=" * 60)
    
    while True:
        puerto = detectar_puerto()
        if not puerto:
            print("⏳ Esperando a que se conecte el adaptador...")
            time.sleep(5)
            continue

        try:
            with serial.Serial(puerto, BAUDIOS, timeout=1) as ser:
                print(f"📡 Escuchando en {puerto} a {BAUDIOS} baudios...")
                print("🔄 Esperando eventos de la central ID3000...\n")
                last_line_time = None
                
                while True:
                    line = ser.readline().decode("utf-8", errors="ignore").strip()
                    if line:
                        # Si es el inicio de un nuevo evento, procesar el evento anterior
                        if es_inicio_evento(line) and evento_buffer:
                            procesar_evento_completo(evento_buffer)
                            evento_buffer = []
                        
                        # Añadir la línea al buffer
                        evento_buffer.append(line)
                        last_line_time = time.time()
                        
                        # Si el buffer tiene más de 10 líneas, procesar (evento muy largo)
                        if len(evento_buffer) > 10:
                            procesar_evento_completo(evento_buffer)
                            evento_buffer = []
                    else:
                        # Si no llegaron líneas y hay inactividad suficiente, enviar evento acumulado
                        if evento_buffer and last_line_time and (time.time() - last_line_time > IDLE_TIMEOUT_SECONDS):
                            procesar_evento_completo(evento_buffer)
                            evento_buffer = []

        except serial.SerialException as e:
            error_msg = str(e)
            print(f"❌ Error con el puerto {puerto}: {error_msg}")
            # Antes de reiniciar/esperar, si hay un evento pendiente en buffer, enviarlo
            if evento_buffer:
                try:
                    procesar_evento_completo(evento_buffer)
                finally:
                    evento_buffer = []

            # Si detecta el fallo del chip Prolific → reinicia con PnPUtil
            if ("PL2303" in error_msg or 
                "not prolific" in error_msg.lower() or 
                "this is not prolific" in error_msg.lower()):
                print("🚨 Detectado error de chip Prolific - iniciando reinicio automático...")
                reiniciar_dispositivo()

            print("⏳ Reintentando en 5 segundos...\n")
            time.sleep(5)

if __name__ == "__main__":
    try:
        iniciar_listener()
    except KeyboardInterrupt:
        print("\n🛑 Listener detenido manualmente")
        print("📊 Estadísticas:")
        print(f"   - Último evento procesado: {ultimo_evento_id}")
        # Enviar cualquier evento que haya quedado en buffer
        if evento_buffer:
            try:
                procesar_evento_completo(evento_buffer)
            finally:
                evento_buffer = []
        print(f"   - Buffer actual: {len(evento_buffer)} líneas")
