"""
Sistema de Lectura Automática de Medidores con CLI Colorida y Alertas

Este script implementa un backend para la lectura automática de medidores de energía eléctrica,
con una interfaz de línea de comandos (CLI) colorida para control en tiempo de ejecución y un sistema de alertas.

Instrucciones de uso:
1. Asegúrese de tener Python 3.6 o superior instalado.
2. Instale las dependencias requeridas: pip install prompt_toolkit colorama
3. Modifique la sección 'if __name__ == "__main__":' al final del archivo para configurar sus conexiones iniciales.
4. Ejecute el script con: python3 ev_ami_backend.py

Comandos CLI disponibles:
- add <host> <port> <mode> <meter_type> [--interrogation] [--frequency <seconds>]: Añade una nueva conexión
- list: Lista todas las conexiones activas
- remove <index>: Elimina una conexión por su índice
- status: Muestra el estado de todas las conexiones
- exit: Cierra el programa

El sistema de alertas notificará problemas de conexión y datos anómalos en la consola con colores distintivos.
"""

import socket
import json
import threading
import time
import logging
from abc import ABC, abstractmethod
from datetime import datetime
import re
from prompt_toolkit import PromptSession
from prompt_toolkit.patch_stdout import patch_stdout
from prompt_toolkit.formatted_text import HTML
from colorama import init, Fore, Back, Style

# Inicializar colorama
init(autoreset=True)

# Configuración de logging con colores
class ColoredFormatter(logging.Formatter):
    FORMATS = {
        logging.DEBUG: Fore.CYAN + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + Style.RESET_ALL,
        logging.INFO: Fore.GREEN + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + Style.RESET_ALL,
        logging.WARNING: Fore.YELLOW + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + Style.RESET_ALL,
        logging.ERROR: Fore.RED + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + Style.RESET_ALL,
        logging.CRITICAL: Fore.RED + Back.WHITE + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + Style.RESET_ALL
    }

    def format(self, record):
        log_fmt = self.FORMATS.get(record.levelno)
        formatter = logging.Formatter(log_fmt)
        return formatter.format(record)

handler = logging.StreamHandler()
handler.setFormatter(ColoredFormatter())
logging.basicConfig(level=logging.INFO, handlers=[handler])

# ... [El resto de las clases MeterProtocol e IEC62056Protocol permanecen sin cambios] ...

class AlertSystem:
    """Sistema de alertas para notificar problemas y datos anómalos."""

    def __init__(self):
        self.logger = logging.getLogger("AlertSystem")

    def connection_error(self, host, port, error):
        """Notifica un error de conexión."""
        self.logger.error(f"{Fore.RED}ALERTA: Error de conexión en {host}:{port} - {error}{Style.RESET_ALL}")

    def data_anomaly(self, host, port, message):
        """Notifica una anomalía en los datos recibidos."""
        self.logger.warning(f"{Fore.YELLOW}ALERTA: Anomalía detectada en datos de {host}:{port} - {message}{Style.RESET_ALL}")

class MeterConnection:
    """Maneja la conexión y comunicación con un medidor específico."""

    def __init__(self, host, port, mode, meter_type, alert_system, interrogation_enabled=False, interrogation_frequency=60):
        # ... [El resto del método __init__ permanece sin cambios] ...
        self.logger = logging.getLogger(f"MeterConnection-{host}:{port}")

    # ... [El resto de los métodos permanecen sin cambios] ...

    def save_data(self, data):
        """Guarda los datos procesados en un archivo."""
        timestamp = datetime.now().isoformat()
        json_data = json.dumps({
            "timestamp": timestamp,
            "meter": f"{self.host}:{self.port}",
            "data": data
        }, indent=2)
        with open("meter_readings.txt", "a") as f:
            f.write(json_data + "\n")
        self.logger.info(f"{Fore.GREEN}Datos guardados: {json_data}{Style.RESET_ALL}")

        # Ejemplo simple de detección de anomalías
        if "OBIS" in data and "1.8.0" in data["OBIS"] and float(data["OBIS"].split("1.8.0(")[1].split("*")[0]) > 1000000:
            self.alert_system.data_anomaly(self.host, self.port, "Consumo de energía anormalmente alto")

class MeterReadingBackend:
    """Gestiona múltiples conexiones de medidores."""

    def __init__(self):
        self.connections = []
        self.alert_system = AlertSystem()

    def add_connection(self, host, port, mode, meter_type, interrogation_enabled=False, interrogation_frequency=60):
        # ... [El resto del método permanece sin cambios] ...
        print(f"{Fore.GREEN}Conexión añadida: {host}:{port}{Style.RESET_ALL}")

    def remove_connection(self, index):
        """Elimina una conexión por su índice."""
        if 0 <= index < len(self.connections):
            connection = self.connections.pop(index)
            # Aquí deberíamos agregar lógica para cerrar la conexión de manera segura
            print(f"{Fore.YELLOW}Conexión {connection.host}:{connection.port} eliminada.{Style.RESET_ALL}")
        else:
            print(f"{Fore.RED}Índice de conexión inválido.{Style.RESET_ALL}")

    def list_connections(self):
        """Lista todas las conexiones activas."""
        for i, conn in enumerate(self.connections):
            print(f"{Fore.CYAN}{i}: {conn.host}:{conn.port} - {conn.mode} - {conn.meter_type}{Style.RESET_ALL}")

    def status(self):
        """Muestra el estado de todas las conexiones."""
        for i, conn in enumerate(self.connections):
            status = f"{Fore.GREEN}Conectado{Style.RESET_ALL}" if conn.connected else f"{Fore.RED}Desconectado{Style.RESET_ALL}"
            print(f"{Fore.CYAN}{i}: {conn.host}:{conn.port} - {status}{Style.RESET_ALL}")

    def run_cli(self):
        """Ejecuta la interfaz de línea de comandos."""
        session = PromptSession()
        
        print(f"{Fore.MAGENTA}{Style.BRIGHT}Bienvenido al Sistema de Lectura Automática de Medidores{Style.RESET_ALL}")
        print(f"{Fore.CYAN}Use 'help' para ver los comandos disponibles.{Style.RESET_ALL}")
        
        while True:
            try:
                with patch_stdout():
                    command = session.prompt(HTML('<ansigreen>AMI Backend></ansigreen> '))
                
                if command.startswith("add"):
                    parts = command.split()
                    if len(parts) < 5:
                        print(f"{Fore.YELLOW}Uso: add <host> <port> <mode> <meter_type> [--interrogation] [--frequency <seconds>]{Style.RESET_ALL}")
                    else:
                        host, port, mode, meter_type = parts[1:5]
                        interrogation = "--interrogation" in parts
                        frequency = 60
                        if "--frequency" in parts:
                            frequency = int(parts[parts.index("--frequency") + 1])
                        self.add_connection(host, int(port), mode, meter_type, interrogation, frequency)

                elif command == "list":
                    self.list_connections()

                elif command.startswith("remove"):
                    parts = command.split()
                    if len(parts) != 2:
                        print(f"{Fore.YELLOW}Uso: remove <index>{Style.RESET_ALL}")
                    else:
                        self.remove_connection(int(parts[1]))

                elif command == "status":
                    self.status()

                elif command == "help":
                    print(f"{Fore.CYAN}Comandos disponibles:{Style.RESET_ALL}")
                    print(f"  {Fore.GREEN}add <host> <port> <mode> <meter_type> [--interrogation] [--frequency <seconds>]{Style.RESET_ALL}: Añade una nueva conexión")
                    print(f"  {Fore.GREEN}list{Style.RESET_ALL}: Lista todas las conexiones activas")
                    print(f"  {Fore.GREEN}remove <index>{Style.RESET_ALL}: Elimina una conexión por su índice")
                    print(f"  {Fore.GREEN}status{Style.RESET_ALL}: Muestra el estado de todas las conexiones")
                    print(f"  {Fore.GREEN}exit{Style.RESET_ALL}: Cierra el programa")

                elif command == "exit":
                    break

                else:
                    print(f"{Fore.RED}Comando no reconocido. Use 'help' para ver los comandos disponibles.{Style.RESET_ALL}")

            except KeyboardInterrupt:
                continue
            except EOFError:
                break

        print(f"{Fore.MAGENTA}{Style.BRIGHT}Saliendo del programa...{Style.RESET_ALL}")

if __name__ == "__main__":
    backend = MeterReadingBackend()
    
    # Configurar las conexiones de los medidores
    # Formato: backend.add_connection(host, port, mode, meter_type, interrogation_enabled, interrogation_frequency)
    backend.add_connection("35.247.249.153", 48306, "client", "IEC62056", interrogation_enabled=True, interrogation_frequency=600)
    backend.add_connection("35.247.249.153", 48316, "client", "IEC62056", interrogation_enabled=True, interrogation_frequency=600)
    backend.add_connection("35.247.249.153", 48307, "client", "IEC62056", interrogation_enabled=True, interrogation_frequency=60)
    
    # Iniciar la CLI
    backend.run_cli()