import logging
import os
from datetime import datetime, timedelta, timezone
from database.connection import DatabaseConnection

class EstadoMonitor:
    """Monitorea el estado de los medidores basado en la última medición recibida"""
    
    def __init__(self):
        self.db = DatabaseConnection()
        # Obtener el tiempo sin reporte desde variables de entorno
        self.tiempo_sin_reporte_horas = int(os.getenv('TIEMPO_SIN_REPORTE_HORAS', 36))
        
        # Estados definidos según la tabla estados
        self.ESTADO_NORMAL = 2
        self.ESTADO_FALTA_REPORTE = 4
    
    def actualizar_estados_medidores(self):
        """
        Función principal que actualiza los estados de todos los medidores
        en la tabla tiempo_real basado en la última medición recibida
        """
        try:
            logging.info(f"Iniciando actualización de estados de medidores...")
            logging.info(f"Tiempo sin reporte configurado: {self.tiempo_sin_reporte_horas} horas")
            
            # Conectar a la base de datos
            if not self.db.connect():
                logging.error("No se pudo conectar a la base de datos")
                return {
                    'success': False,
                    'error': 'Error de conexión a la base de datos'
                }
            
            # Obtener las últimas mediciones de cada medidor
            ultimas_mediciones = self._obtener_ultimas_mediciones()
            
            if not ultimas_mediciones:
                logging.warning("No se encontraron mediciones para procesar")
                return {
                    'success': True,
                    'medidores_procesados': 0,
                    'medidores_normales': 0,
                    'medidores_sin_reporte': 0
                }
            
            # Procesar cada medidor y actualizar su estado
            resultado = self._procesar_y_actualizar_estados(ultimas_mediciones)
            
            logging.info(f"Actualización completada. Procesados: {resultado['medidores_procesados']}, "
                        f"Normales: {resultado['medidores_normales']}, "
                        f"Sin reporte: {resultado['medidores_sin_reporte']}")
            
            return resultado
            
        except Exception as e:
            logging.error(f"Error durante la actualización de estados: {e}")
            return {
                'success': False,
                'error': str(e)
            }
        finally:
            self.db.disconnect()
    
    def _obtener_ultimas_mediciones(self):
        """
        Obtiene la última medición de cada medidor (la más reciente por fecha_recepcion)
        """
        try:
            cursor = self.db.get_cursor()
            
            # Query para obtener la última medición de cada medidor (excluir eliminados)
            query = """
                SELECT 
                    m1.idMedidor,
                    m1.fecha_recepcion,
                    med.nombre as nombre_medidor
                FROM mediciones m1
                INNER JOIN medidores med ON m1.idMedidor = med.idMedidor
                INNER JOIN (
                    SELECT idMedidor, MAX(fecha_recepcion) as max_fecha
                    FROM mediciones
                    GROUP BY idMedidor
                ) m2 ON m1.idMedidor = m2.idMedidor AND m1.fecha_recepcion = m2.max_fecha
                WHERE med.deleted_at IS NULL
                ORDER BY m1.idMedidor
            """
            
            cursor.execute(query)
            resultados = cursor.fetchall()
            
            logging.info(f"Se encontraron {len(resultados)} medidores con mediciones")
            
            # Convertir a lista de diccionarios para facilitar el manejo
            ultimas_mediciones = []
            for row in resultados:
                ultimas_mediciones.append({
                    'idMedidor': row[0],
                    'fecha_recepcion': row[1],
                    'nombre_medidor': row[2]
                })
            
            return ultimas_mediciones
            
        except Exception as e:
            logging.error(f"Error obteniendo últimas mediciones: {e}")
            return []
    
    def _procesar_y_actualizar_estados(self, ultimas_mediciones):
        """
        Procesa cada medidor y actualiza su estado en la tabla tiempo_real
        """
        medidores_procesados = 0
        medidores_normales = 0
        medidores_sin_reporte = 0
        
        # Obtener la fecha/hora actual en UTC-3 (mismo timezone que las mediciones)
        ahora = datetime.now(timezone(timedelta(hours=-3)))
        limite_tiempo = ahora - timedelta(hours=self.tiempo_sin_reporte_horas)
        
        logging.info(f"Fecha actual: {ahora}")
        logging.info(f"Límite para considerar sin reporte: {limite_tiempo}")
        
        try:
            cursor = self.db.get_cursor()
            
            for medicion in ultimas_mediciones:
                id_medidor = medicion['idMedidor']
                fecha_recepcion = medicion['fecha_recepcion']
                nombre_medidor = medicion['nombre_medidor']
                
                # Asegurar que fecha_recepcion tenga timezone
                if fecha_recepcion.tzinfo is None:
                    # Asumir que está en UTC-3 como las demás fechas del sistema
                    fecha_recepcion = fecha_recepcion.replace(tzinfo=timezone(timedelta(hours=-3)))
                
                # Determinar el nuevo estado
                if fecha_recepcion >= limite_tiempo:
                    nuevo_estado = self.ESTADO_NORMAL
                    medidores_normales += 1
                    estado_desc = "Normal"
                else:
                    nuevo_estado = self.ESTADO_FALTA_REPORTE
                    medidores_sin_reporte += 1
                    estado_desc = "Falta Reporte"
                
                # Calcular horas desde la última medición
                horas_desde_medicion = (ahora - fecha_recepcion).total_seconds() / 3600
                
                logging.debug(f"Medidor {nombre_medidor} (ID: {id_medidor}): "
                            f"Última medición: {fecha_recepcion}, "
                            f"Horas transcurridas: {horas_desde_medicion:.1f}, "
                            f"Nuevo estado: {estado_desc}")
                
                # Actualizar el estado en la tabla tiempo_real
                self._actualizar_estado_medidor(cursor, id_medidor, nuevo_estado)
                medidores_procesados += 1
            
            # Hacer commit de todos los cambios
            self.db.commit()
            logging.info("Todos los cambios fueron confirmados en la base de datos")
            
            return {
                'success': True,
                'medidores_procesados': medidores_procesados,
                'medidores_normales': medidores_normales,
                'medidores_sin_reporte': medidores_sin_reporte
            }
            
        except Exception as e:
            # Rollback en caso de error
            self.db.rollback()
            logging.error(f"Error procesando estados, rollback realizado: {e}")
            raise e
    
    def _actualizar_estado_medidor(self, cursor, id_medidor, nuevo_estado):
        """
        Actualiza el estado de un medidor específico en la tabla tiempo_real
        """
        try:
            # Verificar si existe el registro en tiempo_real
            query_check = "SELECT COUNT(*) FROM tiempo_real WHERE idMedidor = %s"
            cursor.execute(query_check, (id_medidor,))
            existe = cursor.fetchone()[0] > 0
            
            if existe:
                # Actualizar el estado existente
                query_update = """
                    UPDATE tiempo_real 
                    SET estado = %s 
                    WHERE idMedidor = %s
                """
                cursor.execute(query_update, (nuevo_estado, id_medidor))
                logging.debug(f"Estado actualizado para medidor ID {id_medidor}: estado = {nuevo_estado}")
            else:
                # Crear un nuevo registro en tiempo_real si no existe
                query_insert = """
                    INSERT INTO tiempo_real 
                    (idMedidor, lastReporte, estado, 
                     acumulado, diario, rssi, temperatura, cumulFlowUnit, dailyFlowUnit)
                    VALUES 
                    (%s, NOW(), %s, 0, 0, 0, 0, 0, 0)
                """
                cursor.execute(query_insert, (id_medidor, nuevo_estado))
                logging.debug(f"Nuevo registro creado en tiempo_real para medidor ID {id_medidor}: estado = {nuevo_estado}")
                
        except Exception as e:
            logging.error(f"Error actualizando estado del medidor {id_medidor}: {e}")
            raise e
    
    def obtener_resumen_estados(self):
        """
        Obtiene un resumen del estado actual de todos los medidores
        """
        try:
            if not self.db.connect():
                return None
            
            cursor = self.db.get_cursor()
            
            query = """
                SELECT 
                    e.nombre as estado_nombre,
                    COUNT(*) as cantidad
                FROM tiempo_real tr
                INNER JOIN estados e ON tr.estado = e.idEstado
                INNER JOIN medidores med ON tr.idMedidor = med.idMedidor
                WHERE med.deleted_at IS NULL
                GROUP BY tr.estado, e.nombre
                ORDER BY tr.estado
            """
            
            cursor.execute(query)
            resultados = cursor.fetchall()
            
            resumen = {}
            for row in resultados:
                resumen[row[0]] = row[1]
            
            return resumen
            
        except Exception as e:
            logging.error(f"Error obteniendo resumen de estados: {e}")
            return None
        finally:
            self.db.disconnect() 