# mesh_registra_clase.py

import mysql.connector
from mysql.connector import Error
import json
import os
from datetime import datetime


# ------------------------------------------------------------
# 🔧 Cargar configuración de base de datos
# ------------------------------------------------------------
_ruta_config = os.path.join(os.path.dirname(__file__), 'base-config.json')
with open(_ruta_config, 'r', encoding='utf-8') as f:
    _config_datos = json.load(f)

DB_CONFIG = _config_datos['DB_CONFIG']


# ------------------------------------------------------------
# 🗃️ Clase principal: RegistradorMensajes
# ------------------------------------------------------------
class RegistradorMensajes:
    def __init__(self):
        self.conn = None
        self._conectar()

    def _conectar(self):
        """Establece una conexión reutilizable a la base de datos."""
        try:
            self.conn = mysql.connector.connect(**DB_CONFIG)
        except Error as e:
            print(f"❌ Error al conectar a la base de datos: {e}")
            self.conn = None

    def _asegurar_conexion(self):
        """Reconecta si la conexión está caída."""
        if self.conn is None or not self.conn.is_connected():
            self._conectar()
        return self.conn and self.conn.is_connected()

    def _safe_float(self, val, default=0.0):
        if val is None:
            return float(default)
        try:
            return float(val)
        except (ValueError, TypeError):
            return float(default)

    # --------------------------------------------------------
    # 📝 Métodos de registro por tipo de mensaje
    # --------------------------------------------------------

    def registrar_posicion(self, datos):
        """
        Registra una posición.
        Espera un dict con: id_nodo, canal_nombre, latitud, longitud,
        altitud, fecha_gps, satelites, velocidad.
        """
        if not self._asegurar_conexion():
            return False

        try:
            cursor = self.conn.cursor(dictionary=True)

            # Convertir fecha_gps a naive datetime si es string
            fecha_gps = datos.get('fecha_gps')
            if isinstance(fecha_gps, str):
                fecha_gps = datetime.fromisoformat(fecha_gps.replace('Z', '+00:00')).replace(tzinfo=None)

            # Buscar último registro (excluyendo 'Manual')
            query_ultimo = """
                SELECT id, latitud, longitud, altitud, satelites, velocidad, fecha_gps
                FROM posiciones
                WHERE id_nodo = %s AND canal != 'Manual'
                ORDER BY fecha_gps DESC
                LIMIT 1
            """
            cursor.execute(query_ultimo, (datos['id_nodo'],))
            ultimo = cursor.fetchone()

            # Valores actuales
            lat_actual = round(self._safe_float(datos.get('latitud')), 6)
            lon_actual = round(self._safe_float(datos.get('longitud')), 6)
            alt_actual = self._safe_float(datos.get('altitud'), 0)
            sats_actual = self._safe_float(datos.get('satelites'), 0)
            speed_actual = self._safe_float(datos.get('velocidad'), 0)

            if ultimo:
                lat_ultimo = round(self._safe_float(ultimo['latitud'], 0), 6)
                lon_ultimo = round(self._safe_float(ultimo['longitud'], 0), 6)
                alt_ultimo = self._safe_float(ultimo['altitud'], 0)
                sats_ultimo = self._safe_float(ultimo['satelites'], 0)
                speed_ultimo = self._safe_float(ultimo['velocidad'], 0)

                sin_cambios = (
                    lat_actual == lat_ultimo and
                    lon_actual == lon_ultimo and
                    alt_actual == alt_ultimo and
                    sats_actual == sats_ultimo and
                    speed_actual == speed_ultimo
                )

                if sin_cambios:
                    ult_fecha = ultimo['fecha_gps']
                    if isinstance(ult_fecha, str):
                        ult_fecha = datetime.strptime(ult_fecha, '%Y-%m-%d %H:%M:%S')
                    elif hasattr(ult_fecha, 'tzinfo') and ult_fecha.tzinfo is not None:
                        ult_fecha = ult_fecha.replace(tzinfo=None)
                    if fecha_gps and fecha_gps > ult_fecha:
                        cursor.execute("UPDATE posiciones SET fecha_gps = %s WHERE id = %s", (fecha_gps, ultimo['id']))
                        self.conn.commit()
                        return True
                    else:
                        return False  # No se actualiza

            # Insertar nuevo registro
            cursor.execute("""
                INSERT INTO posiciones 
                (id_nodo, nombre_corto, canal, latitud, longitud, altitud, fecha_gps, satelites, velocidad)
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
            """, (
                datos['id_nodo'],
                datos.get('nombre_corto') or datos['id_nodo'][-4:].upper(),
                datos['canal_nombre'],
                lat_actual,
                lon_actual,
                alt_actual,
                fecha_gps or datetime.utcnow(),
                sats_actual,
                speed_actual
            ))
            self.conn.commit()
            return True

        except Exception as e:
            print(f"❌ Error al registrar posición: {e}")
            return False
        finally:
            if 'cursor' in locals():
                cursor.close()

    def registrar_telemetria(self, datos):
        """
        Registra telemetría (un solo registro por nodo).
        Espera un dict con: id_nodo, bateria, voltaje, uso_canal,
        transmision, temperatura, humedad, presion.
        """
        if not self._asegurar_conexion():
            return False

        try:
            cursor = self.conn.cursor()

            def es_valido(val):
                return val is not None and val != 0

            # Preparar valores
            valores = [
                datos['id_nodo'],
                datos.get('bateria') if es_valido(datos.get('bateria')) else None,
                datos.get('voltaje') if es_valido(datos.get('voltaje')) else None,
                datos.get('uso_canal') if es_valido(datos.get('uso_canal')) else None,
                datos.get('transmision') if es_valido(datos.get('transmision')) else None,
                datos.get('temperatura') if es_valido(datos.get('temperatura')) else None,
                datos.get('humedad') if es_valido(datos.get('humedad')) else None,
                datos.get('presion') if es_valido(datos.get('presion')) else None
            ]

            # Construir cláusula de actualización
            update_parts = []
            if es_valido(datos.get('bateria')):
                update_parts.append("bateria = VALUES(bateria)")
            if es_valido(datos.get('voltaje')):
                update_parts.append("voltaje = VALUES(voltaje)")
            if es_valido(datos.get('uso_canal')):
                update_parts.append("uso_canal = VALUES(uso_canal)")
            if es_valido(datos.get('transmision')):
                update_parts.append("transmision = VALUES(transmision)")
            if es_valido(datos.get('temperatura')):
                update_parts.append("temperatura = VALUES(temperatura)")
            if es_valido(datos.get('humedad')):
                update_parts.append("humedad = VALUES(humedad)")
            if es_valido(datos.get('presion')):
                update_parts.append("presion = VALUES(presion)")

            if not update_parts:
                return False

            query = f"""
                INSERT INTO telemetria 
                (id_nodo, bateria, voltaje, uso_canal, transmision, temperatura, humedad, presion)
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
                ON DUPLICATE KEY UPDATE {', '.join(update_parts)}
            """
            cursor.execute(query, valores)
            self.conn.commit()
            return True

        except Exception as e:
            print(f"❌ Error al registrar telemetría: {e}")
            return False
        finally:
            if 'cursor' in locals():
                cursor.close()

    def registrar_usuario(self, datos):
        """
        Registra o actualiza información de nodo.
        Espera un dict con: id_nodo, nombre_largo, nombre_corto,
        mac, modelo, rol, firmware.
        """
        if not self._asegurar_conexion():
            return False

        try:
            cursor = self.conn.cursor()
            cursor.execute("""
                INSERT INTO usuarios 
                (id_nodo, nombre, shortName, mac, modelo, rol, firmware)
                VALUES (%s, %s, %s, %s, %s, %s, %s)
                ON DUPLICATE KEY UPDATE
                    nombre = VALUES(nombre),
                    shortName = VALUES(shortName),
                    mac = VALUES(mac),
                    modelo = VALUES(modelo),
                    rol = VALUES(rol),
                    firmware = VALUES(firmware)
            """, (
                datos['id_nodo'],
                datos.get('nombre_largo', 'Sin nombre'),
                datos.get('nombre_corto', datos['id_nodo'][-4:].upper()),
                datos.get('mac', '00:00:00:00:00:00'),
                datos.get('modelo', 'Desconocido'),
                datos.get('rol', 'Desconocido'),
                datos.get('firmware')
            ))
            self.conn.commit()
            return True

        except Exception as e:
            print(f"❌ Error al registrar usuario: {e}")
            return False
        finally:
            if 'cursor' in locals():
                cursor.close()

    def registrar_texto(self, datos):
        """
        Registra un mensaje de texto.
        Espera un dict con: texto, id_nodo, id_recep, canal_nombre.
        """
        if not self._asegurar_conexion():
            return False

        try:
            cursor = self.conn.cursor()
            texto = datos['texto'][:500]  # límite de la tabla
            cursor.execute("""
                INSERT INTO mensajes 
                (texto, id_nodo, id_recep, canal, fecha)
                VALUES (%s, %s, %s, %s, NOW())
            """, (
                texto,
                datos['id_nodo'],
                datos['id_recep'],
                datos['canal_nombre']
            ))
            self.conn.commit()
            return True

        except Exception as e:
            print(f"❌ Error al registrar texto: {e}")
            return False
        finally:
            if 'cursor' in locals():
                cursor.close()

    def cerrar(self):
        """Cierra la conexión a la base de datos."""
        if self.conn and self.conn.is_connected():
            self.conn.close()