# src/db/database.py
"""
Módulo de acceso a datos - MVP v1.0

Implementa todas las operaciones de base de datos del sistema:
- Conexión y gestión de sesiones
- Consultas de mediciones y sensores
- Operaciones de actualización
- Gestión de transacciones

Este módulo centraliza el acceso a datos para mantener consistencia
y facilitar optimizaciones.
"""

import mysql.connector
from mysql.connector import Error, connect
from datetime import datetime, timedelta
import time
import logging
from ..config.settings import DB_CONFIG, BATCH_SIZE

logger = logging.getLogger(__name__)

def connect(db_name):
    """
    Establece conexión con la base de datos con reintentos.
    
    Args:
        db_name (str): Nombre de la base de datos
        
    Returns:
        Connection: Objeto de conexión o None si falla
    """
    retry_count = 0
    while retry_count < DB_CONFIG['retry_attempts']:
        try:
            connection = mysql.connector.connect(
                host=DB_CONFIG['host'],
                port=DB_CONFIG['port'],
                user=DB_CONFIG['user'],
                password=DB_CONFIG['password'],
                database=db_name
            )
            if connection.is_connected():
                logger.info(f"Conectado a la base de datos: {db_name}")
                return connection
        except Error as e:
            logger.error(f"Intento {retry_count + 1} fallido: {e}")
            retry_count += 1
            time.sleep(DB_CONFIG['retry_delay'])
    
    logger.error("No se pudo establecer conexión a la base de datos")
    return None

def get_active_sensors(conn):
    """
    Obtiene lista de sensores activos.
    
    Returns:
        list: [(id_modbus, dmac, id_entrada), ...]
    """
    if not conn:
        return []
        
    try:
        cursor = conn.cursor()
        query = """
        SELECT 
            m.idModbus,
            m.dmac,
            e.idEntrada
        FROM modbus m
        JOIN entradas e ON e.idModbus = m.idModbus
        WHERE m.estado = 1 
        AND m.dmac IS NOT NULL
        """
        cursor.execute(query)
        return cursor.fetchall()
    except Error as e:
        logger.error(f"Error obteniendo sensores activos: {e}")
        return []
    finally:
        cursor.close()

def get_unprocessed_measurements(conn, debug=False, limit=None):
    """
    Obtiene mediciones no procesadas.
    
    Args:
        conn: Conexión a la base de datos
        debug (bool): Flag para modo debug
        limit (int, opcional): Límite de registros a obtener
    """
    if not conn:
        return []
        
    cursor = None
    try:
        if debug:
            logger.debug("Iniciando get_unprocessed_measurements")
            logger.debug(f"Estado conexión: {conn.is_connected()}")
            
        cursor = conn.cursor(buffered=True)
        
        if debug:
            logger.debug("Cursor creado")
            
        try:
            conn.rollback()
            if debug:
                logger.debug("Rollback previo ejecutado")
        except Exception as e:
            if debug:
                logger.debug(f"Error en rollback previo: {e}")
            
        conn.start_transaction()
        if debug:
            logger.debug("Nueva transacción iniciada")
            
        query = """
        SELECT 
            b.id,
            b.dmac,
            b.temp/10.0 as temp,
            b.fecha,
            e.idEntrada
        FROM bandeja_blue b
        JOIN modbus m ON b.dmac = m.dmac
        JOIN entradas e ON e.idModbus = m.idModBus
        WHERE b.procesado_el IS NULL
        AND b.temp IS NOT NULL
        ORDER BY b.fecha ASC
        """
        
        if limit:
            query += f" LIMIT {limit}"
            
        if debug:
            logger.debug(f"Ejecutando query: {query}")
            
        cursor.execute(query)
        results = cursor.fetchall()
        
        if debug:
            logger.debug(f"Resultados obtenidos: {len(results)}")
        
        # Si hay resultados, los marcamos inmediatamente
        if results:
            ids = [row[0] for row in results]
            mark_query = """
            UPDATE bandeja_blue 
            SET procesado_el = NOW()
            WHERE id IN ({})
            AND procesado_el IS NULL
            """.format(','.join(['%s'] * len(ids)))
            
            if debug:
                logger.debug(f"Ejecutando marca de procesados: {len(ids)} registros")
                
            cursor.execute(mark_query, ids)
            
        conn.commit()
        if debug:
            logger.debug("Commit ejecutado exitosamente")
            
        return results
        
    except Error as e:
        logger.error(f"Error obteniendo mediciones: {e}")
        if debug:
            logger.debug("Stack trace completo:", exc_info=True)
        try:
            conn.rollback()
            if debug:
                logger.debug("Rollback ejecutado después de error")
        except:
            if debug:
                logger.debug("Error ejecutando rollback post-error")
        return []
    finally:
        if cursor:
            cursor.close()
            if debug:
                logger.debug("Cursor cerrado")


def mark_as_processed_batch(conn, ids):
    """
    Marca un lote de mediciones como procesadas.
    """
    if not conn or not ids:
        return False
        
    cursor = None
    try:
        cursor = conn.cursor(buffered=True)
        conn.start_transaction()
        
        placeholders = ','.join(['%s'] * len(ids))
        query = f"""
        UPDATE bandeja_blue 
        SET procesado_el = NOW()
        WHERE id IN ({placeholders})
        AND procesado_el IS NULL
        """
        cursor.execute(query, ids)
        conn.commit()
        return True
    except Error as e:
        conn.rollback()
        logger.error(f"Error marcando registros como procesados: {e}")
        return False
    finally:
        if cursor:
            cursor.close()

def insert_registros_batch(conn, registros):
    """
    Inserta lote de registros estadísticos.
    
    Args:
        conn: Conexión a base de datos
        registros (list): Lista de tuplas (idSistema, idEntrada, fecha, ...)
        
    Returns:
        bool: True si la operación fue exitosa
    """
    if not conn or not registros:
        return False
        
    try:
        cursor = conn.cursor()
        query = """
        INSERT INTO registros 
        (idSistema, idEntrada, fecha, medio, maximo, minimo, desvio)
        VALUES (%s, %s, %s, %s, %s, %s, %s)
        """
        cursor.executemany(query, registros)
        conn.commit()
        return True
    except Error as e:
        logger.error(f"Error insertando registros estadísticos: {e}")
        conn.rollback()
        return False
    finally:
        cursor.close()