"""
Modelo Entrada - MVP v1.0

Representa un punto de medición y gestiona el procesamiento de sus datos por períodos.
Cada entrada corresponde a un sensor físico y maneja la acumulación y cálculo
de estadísticas de temperatura en intervalos de 15 minutos.
"""

from datetime import datetime, timedelta
import logging
import math
from ..config.settings import PROCESS_INTERVAL, TEMP_MIN, TEMP_MAX

logger = logging.getLogger(__name__)

class Entrada:
    """
    Clase que representa un punto de medición.
    
    Maneja la recolección de mediciones y cálculo de estadísticas por períodos
    de 15 minutos. Incluye validaciones de datos y gestión de períodos.
    """
    
    def __init__(self, id_entrada, dmac, fecha_inicio):
        self.id_entrada = id_entrada
        self.dmac = dmac
        
        # Normalizar el período de inicio
        self.periodo_inicio = self._normalizar_fecha(fecha_inicio)
        self.periodo_fin = self.periodo_inicio + timedelta(minutes=PROCESS_INTERVAL)
        
        # Inicializar estadísticas
        self.valor_acumulado = 0
        self.valor_maximo = float('-inf')
        self.valor_minimo = float('inf')
        self.mediciones = []
        self.cantidad_mediciones = 0
        self.ultima_medicion = None

    def _normalizar_fecha(self, fecha):
        """
        Normaliza una fecha al cuarto de hora más cercano.
        
        Args:
            fecha (datetime): Fecha a normalizar
            
        Returns:
            datetime: Fecha normalizada al cuarto de hora
        """
        return fecha.replace(
            minute=(fecha.minute // PROCESS_INTERVAL) * PROCESS_INTERVAL,
            second=0,
            microsecond=0
        )

    def agregar_medicion(self, valor, fecha):
        """
        Agrega una nueva medición y valida que corresponda al período actual.
        
        Args:
            valor (float): Valor de temperatura
            fecha (datetime): Timestamp de la medición
            
        Returns:
            bool: True si la medición fue agregada exitosamente
        """
        # Validar rango de temperatura
        if not TEMP_MIN <= valor <= TEMP_MAX:
            logger.warning(
                f"Temperatura fuera de rango para entrada {self.dmac}: {valor}°C"
            )
            return False
            
        # Validar período
        if fecha < self.periodo_inicio or fecha >= self.periodo_fin:
            logger.warning(
                f"Medición fuera de período para entrada {self.dmac}: "
                f"fecha={fecha:%Y-%m-%d %H:%M}, "
                f"período={self.periodo_inicio:%Y-%m-%d %H:%M}-{self.periodo_fin:%H:%M}"
            )
            return False

        # Agregar medición
        self.mediciones.append((valor, fecha))
        self.valor_acumulado += valor
        self.valor_maximo = max(self.valor_maximo, valor)
        self.valor_minimo = min(self.valor_minimo, valor)
        self.cantidad_mediciones += 1
        self.ultima_medicion = fecha
        
        logger.debug(
            f"Medición agregada - DMAC: {self.dmac}, "
            f"Valor: {valor}°C, Total: {self.cantidad_mediciones}"
        )
        
        return True

    def get_estadisticas(self):
        """
        Calcula las estadísticas del período actual.
        
        Returns:
            dict: Diccionario con estadísticas o None si no hay datos
        """
        if not self.mediciones:
            return None
            
        # Calcular estadísticas
        promedio = self.get_promedio()
        desvio = self.get_desvio()
            
        return {
            'periodo_inicio': self.periodo_inicio,
            'periodo_fin': self.periodo_fin,
            'promedio': promedio,
            'maximo': round(self.valor_maximo, 1),
            'minimo': round(self.valor_minimo, 1),
            'desvio': desvio,
            'cantidad_mediciones': self.cantidad_mediciones
        }

    def get_promedio(self):
        """
        Calcula el promedio del período actual.
        
        Returns:
            float: Temperatura promedio o None si no hay mediciones
        """
        if not self.mediciones:
            return None
        return round(self.valor_acumulado / self.cantidad_mediciones, 1)

    def get_desvio(self):
        """
        Calcula la desviación estándar del período.
        
        Returns:
            float: Desviación estándar o 0 si hay menos de 2 mediciones
        """
        if len(self.mediciones) < 2:
            return 0
        
        promedio = self.get_promedio()
        suma_cuadrados = sum((x[0] - promedio) ** 2 for x in self.mediciones)
        return round(math.sqrt(suma_cuadrados / (len(self.mediciones) - 1)), 2)

    def debe_cerrar_por_timeout(self, fecha_actual):
        """
        Verifica si el período debe cerrarse por timeout.
        
        Args:
            fecha_actual (datetime): Timestamp actual
            
        Returns:
            bool: True si el período debe cerrarse
        """
        return fecha_actual > self.periodo_fin + timedelta(minutes=1)

    def reiniciar_periodo(self, fecha_inicio=None):
        """
        Reinicia el período para comenzar uno nuevo.
        
        Args:
            fecha_inicio (datetime): Timestamp inicial opcional para el nuevo período
        """
        if fecha_inicio is None:
            # Si no se proporciona fecha, usar el fin del período actual
            nuevo_inicio = self.periodo_fin
        else:
            # Si se proporciona fecha, normalizarla
            nuevo_inicio = self._normalizar_fecha(fecha_inicio)
        
        # Asignar nuevo período
        self.periodo_inicio = nuevo_inicio
        self.periodo_fin = self.periodo_inicio + timedelta(minutes=PROCESS_INTERVAL)
        
        # Reiniciar estadísticas
        self.valor_acumulado = 0
        self.valor_maximo = float('-inf')
        self.valor_minimo = float('inf')
        self.mediciones = []
        self.cantidad_mediciones = 0
        
        logger.debug(
            f"Período reiniciado - DMAC: {self.dmac}, "
            f"Nuevo período: {self.periodo_inicio:%Y-%m-%d %H:%M} - {self.periodo_fin:%H:%M}"
        )

    def __str__(self):
        """Representación en string de la entrada."""
        return (
            f"Entrada {self.id_entrada} (DMAC: {self.dmac}) - "
            f"Período: {self.periodo_inicio:%Y-%m-%d %H:%M} - {self.periodo_fin:%H:%M}"
        )