#!/usr/bin/env python3
import json
import os
import sys
from datetime import datetime
import mysql.connector
from mysql.connector import Error
import paramiko

# === CONFIGURACIÓN ===
CONFIG_FILE = os.path.join(os.path.dirname(__file__), 'servid-config.json')
OUTPUT_FILE = "nodos.json"

def cargar_configuracion():
    with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
        config = json.load(f)
    return config['DB_CONFIG'], config['SFTP_CONFIG']

# === FUNCIÓN: OBTENER DATOS DE LA BASE DE DATOS ===
def conectar_y_obtener_datos(db_config):
    connection = None
    cursor = None
    try:
        connection = mysql.connector.connect(**db_config)
        if connection.is_connected():
            cursor = connection.cursor(dictionary=True)
            query = """
            SELECT 
                u.id_nodo,
                u.nombre,
                u.shortName,
                u.mac,
                u.modelo,
                u.rol,
                u.firmware,
                p.canal,
                p.latitud,
                p.longitud,
                p.altitud,
                p.estado AS estado_ultima_posicion,
                UNIX_TIMESTAMP(p.fecha_recepcion) AS hora,
                t.bateria,
                t.voltaje,
                t.uso_canal,
                t.transmision,
                t.temperatura,
                t.humedad,
                t.presion,
                CASE 
                    WHEN EXISTS (
                        SELECT 1 FROM posiciones px 
                        WHERE px.id_nodo = u.id_nodo AND px.estado = 9
                    ) THEN 1 
                    ELSE 0 
                END AS tiene_estado_9
            FROM usuarios u
            INNER JOIN (
                SELECT 
                    id_nodo,
                    canal,
                    latitud,
                    longitud,
                    altitud,
                    estado,
                    fecha_recepcion,
                    ROW_NUMBER() OVER (PARTITION BY id_nodo ORDER BY fecha_recepcion DESC) AS rn
                FROM posiciones
            ) p ON u.id_nodo = p.id_nodo AND p.rn = 1
            LEFT JOIN (
                SELECT 
                    id_nodo,
                    bateria,
                    voltaje,
                    uso_canal,
                    transmision,
                    temperatura,
                    humedad,
                    presion,
                    ROW_NUMBER() OVER (PARTITION BY id_nodo ORDER BY id DESC) AS rn
                FROM telemetria
                WHERE 
                    bateria IS NOT NULL OR
                    voltaje IS NOT NULL OR
                    uso_canal IS NOT NULL OR
                    transmision IS NOT NULL OR
                    temperatura IS NOT NULL OR
                    humedad IS NOT NULL OR
                    presion IS NOT NULL
            ) t ON u.id_nodo = t.id_nodo AND t.rn = 1
            """
            cursor.execute(query)
            return cursor.fetchall()
    except Error as e:
        print(f"❌ Error MySQL: {e}")
        return None  # ← Cambiado: no sys.exit()
    except Exception as e:
        print(f"❌ Error inesperado: {e}")
        return None  # ← Cambiado
    finally:
        if cursor:
            cursor.close()
        if connection and connection.is_connected():
            connection.close()

def coordenada_es_valida(valor):
    if valor is None:
        return False
    s = str(valor).strip()
    if not s:
        return False
    try:
        f = float(s)
        return f != 0.0
    except (ValueError, TypeError):
        return False

def procesar_nodo(nodo_db):
    resultado = {}
    resultado["id"] = str(nodo_db["id_nodo"])
    nombre = nodo_db.get("nombre") or "sin nombre"
    resultado["nombre"] = str(nombre).strip() or "sin nombre"
    shortName = nodo_db.get("shortName") or "s/n"
    resultado["shortName"] = str(shortName).strip() or "s/n"
    resultado["latitud"] = str(nodo_db["latitud"]).strip()
    resultado["longitud"] = str(nodo_db["longitud"]).strip()
    hora = nodo_db.get("hora")
    if hora is None or hora == 0:
        resultado["ultimo_contacto"] = "0"
    else:
        resultado["ultimo_contacto"] = str(int(hora)) 
    campos_telemetria = {
        "bateria": "bateria",
        "voltaje": "voltaje",
        "uso_canal": "usoCanal",
        "transmision": "transmision",
        "temperatura": "temperatura",
        "humedad": "humedad",
        "presion": "presion",
    }
    otros_campos = ["modelo", "rol", "mac", "canal", "altitud", "firmware"]
    for db_key, json_key in campos_telemetria.items():
        val = nodo_db.get(db_key)
        if val is not None:
            s = str(val).strip()
            if s and s.lower() not in {"null", "none", "sin datos", ""}:
                resultado[json_key] = s
    for campo in otros_campos:
        val = nodo_db.get(campo)
        if val is None:
            continue
        s = str(val).strip()
        if s and s.lower() not in {"null", "none", "sin datos", ""}:
            resultado[campo] = s
    return resultado

def generar_json(db_config):
    print("🔄 Obteniendo la ÚLTIMA posición y telemetría de cada nodo...")
    todos = conectar_y_obtener_datos(db_config)
    if todos is None:
        return False  # ← Falló la conexión
    sin_estado_9 = [n for n in todos if not n.get("tiene_estado_9")]
    validos = [
        n for n in sin_estado_9
        if coordenada_es_valida(n.get("latitud")) and coordenada_es_valida(n.get("longitud"))
    ]
    print(f"📊 Total de nodos únicos: {len(todos)}")
    print(f"🚫 Excluidos por tener estado == 9 en alguna posición: {len(todos) - len(sin_estado_9)}")
    print(f"🚫 Excluidos por coordenadas inválidas: {len(sin_estado_9) - len(validos)}")
    print(f"✅ Nodos exportables: {len(validos)}")
    nodos_json = [procesar_nodo(n) for n in validos]
    fecha_actual = datetime.now().strftime("%d/%m/%Y - %H:%M")
    nodo_fecha = {"id": "fecha", "nombre": fecha_actual}
    nodos_json.insert(0, nodo_fecha)
    try:
        with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
            json.dump(nodos_json, f, indent=2, ensure_ascii=False)
        print(f"✅ Archivo '{OUTPUT_FILE}' generado con éxito.")
        return True
    except Exception as e:
        print(f"❌ Error al escribir el archivo JSON: {e}")
        return False

def subir_archivo_sftp(sftp_config):
    if not os.path.exists(OUTPUT_FILE):
        print(f"❌ Archivo local '{OUTPUT_FILE}' no encontrado.")
        return False
    cliente = paramiko.SSHClient()
    cliente.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        print("🔐 Conectando al servidor SFTP...")
        cliente.connect(
            hostname=sftp_config["host"],
            port=sftp_config["port"],
            username=sftp_config["username"],
            password=sftp_config["password"]
        )
        sftp = cliente.open_sftp()
        print(f"📤 Subiendo {OUTPUT_FILE} a {sftp_config['remote_path']}...")
        sftp.put(OUTPUT_FILE, sftp_config["remote_path"])
        stat = sftp.stat(sftp_config["remote_path"])
        print(f"✅ Archivo subido exitosamente. Tamaño: {stat.st_size} bytes")
        sftp.close()
        return True
    except paramiko.AuthenticationException:
        print("❌ Error: Autenticación fallida. Verifica tu usuario o contraseña.")
        return False
    except paramiko.SSHException as e:
        print(f"❌ Error de conexión SSH: {e}")
        return False
    except Exception as e:
        print(f"❌ Error inesperado: {e}")
        return False
    finally:
        cliente.close()

# === FUNCIÓN PRINCIPAL AJUSTADA ===
def main():
    """Devuelve True si todo salió bien, False en caso de error."""
    print("🚀 Iniciando generación y subida de nodos.json")
    try:
        db_config, sftp_config = cargar_configuracion()
    except Exception as e:
        print(f"❌ Error al cargar la configuración: {e}")
        return False

    if not generar_json(db_config):
        print("❌ Falló la generación del JSON.")
        return False

    if not subir_archivo_sftp(sftp_config):
        print("❌ La subida falló.")
        return False

    print("🎉 Proceso completado con éxito.")
    return True

# Solo ejecuta si se llama directamente
if __name__ == "__main__":
    exit_code = 0 if main() else 1
    sys.exit(exit_code)