"""
Gestión de conexiones individuales con los medidores.

Este módulo maneja la conexión, comunicación y procesamiento de datos para cada medidor individual.

Desarrolladores:
- Para modificar el comportamiento de la conexión, ajuste los métodos de la clase MeterConnection
- Para añadir soporte para nuevos protocolos, actualice el método get_protocol()
- La detección de anomalías se realiza en el método save_data(). Expanda esta lógica según sea necesario.
"""

import socket
import json
import threading
import time
from datetime import datetime
from protocols import IEC62056Protocol
from colorama import Fore, Style
import logging

class MeterConnection:
    def __init__(self, host, port, mode, meter_type, alert_system, interrogation_enabled=False, interrogation_frequency=60):
        """
        Inicializa una conexión de medidor.

        Args:
            host (str): Dirección IP del medidor.
            port (int): Puerto del medidor.
            mode (str): Modo de conexión ("client" o "server").
            meter_type (str): Tipo de protocolo del medidor.
            alert_system (AlertSystem): Sistema de alertas.
            interrogation_enabled (bool): Si se debe interrogar activamente al medidor.
            interrogation_frequency (int): Frecuencia de interrogación en segundos.
        """
        self.host = host
        self.port = port
        self.mode = mode
        self.meter_type = meter_type
        self.sock = None
        self.connected = False
        self.protocol = self.get_protocol(meter_type)
        self.logger = logging.getLogger(f"MeterConnection-{host}:{port}")
        self.alert_system = alert_system
        self.interrogation_enabled = interrogation_enabled
        self.interrogation_frequency = interrogation_frequency

    def get_protocol(self, meter_type):
        """Selecciona el protocolo adecuado basado en el tipo de medidor."""
        protocols = {
            "IEC62056": IEC62056Protocol(),
            # Añadir más protocolos aquí cuando se implementen
        }
        return protocols.get(meter_type, IEC62056Protocol())  # Default to IEC62056 if unknown

    def connect(self):
        """Establece la conexión con el medidor."""
        while not self.connected:
            try:
                self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self.sock.connect((self.host, self.port))
                self.logger.info(f"{Fore.GREEN}Cliente conectado a {self.host}:{self.port}{Style.RESET_ALL}")
                self.connected = True
                if self.interrogation_enabled:
                    threading.Thread(target=self.interrogate_meter, daemon=True).start()
            except Exception as e:
                self.alert_system.connection_error(self.host, self.port, str(e))
                time.sleep(5)

    def interrogate_meter(self):
        """Envía periódicamente solicitudes de estado al medidor."""
        while self.connected:
            try:
                self.sock.sendall(b"estado")
                self.logger.info(f"{Fore.CYAN}Enviada solicitud de estado a {self.host}:{self.port}{Style.RESET_ALL}")
                time.sleep(self.interrogation_frequency)
            except Exception as e:
                self.alert_system.connection_error(self.host, self.port, f"Error al enviar solicitud de estado: {e}")
                self.connected = False
                self.sock.close()
                self.connect()

    def handle_connection(self):
        """Maneja la recepción de datos del medidor."""
        while True:
            try:
                data = self.sock.recv(4096)
                if not data:
                    raise Exception("Conexión cerrada por el otro extremo")
                processed_data = self.protocol.process_message(data.decode('utf-8'))
                self.save_data(processed_data)
            except Exception as e:
                self.alert_system.connection_error(self.host, self.port, str(e))
                self.connected = False
                self.sock.close()
                self.connect()

    def save_data(self, data):
        """
        Guarda los datos procesados en un archivo y detecta anomalías.
        
        Args:
            data (dict): Datos procesados del medidor.
        """
        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
        # Nota: Esta lógica debe ser expandida y refinada según las necesidades específicas
        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")
