import psycopg2
from psycopg2 import sql
from psycopg2.extras import execute_values
from psycopg2.extras import RealDictCursor


class DBForma:
    def __init__(self, db_obj: dict):
        """
        Inicializa la conexión a la base de datos.

        :param db_obj: Diccionario con las credenciales de la base de datos.
        Debe contener: host, port, database, user, password
        """
        self.db_obj = db_obj

    def _get_connection(self):
        """
        Crea y retorna una nueva conexión a la base de datos.
        """
        return psycopg2.connect(
            host=self.db_obj['host'],
            port=self.db_obj['port'],
            database=self.db_obj['database'],
            user=self.db_obj['user'],
            password=self.db_obj['password']
        )

    def login(self, email: str):
        """
        Verifica las credenciales de un usuario.
        
        :param email: Email del usuario a verificar
        :return: Tupla con la contraseña si el usuario existe, None si no existe
        :raises: RuntimeError si hay algún error en la consulta
        """
        # Corrección 1: Usar parámetros correctamente para evitar SQL injection
        query = "SELECT pswd FROM users WHERE email = %s;"
        
        try:
            with self._get_connection() as conn:
                with conn.cursor() as cursor:
                    # Corrección 2: Pasar parámetros como tupla (aunque sea uno solo)
                    cursor.execute(query, (email,))
                    return cursor.fetchone()
        except Exception as e:
            raise RuntimeError(f"Error al verificar credenciales: {e}")
    
    def reset_pswd(self, email: str):
        query = "SELECT email FROM users WHERE email = %s;"
        try:
            with self._get_connection() as conn:
                with conn.cursor() as cursor:
                    cursor.execute(query, (email,))
                    return cursor.fetchone()
        except Exception as e:
            raise RuntimeError(f"Error al verificar credenciales: {e}")
        

    def get_id(self, email: str):
        query = "SELECT id FROM users WHERE email = %s;"
        try:
            with self._get_connection() as conn:
                with conn.cursor() as cursor:
                    cursor.execute(query, (email,))
                    return cursor.fetchone()
        except Exception as e:
            raise RuntimeError(f"Error al verificar credenciales: {e}")
    
    def get_data(self, query: str, data_tuple: tuple):
        try:
            with self._get_connection() as conn:
                with conn.cursor() as cursor:
                    cursor.execute(query, data_tuple)
                    result = cursor.fetchone()
                    return result
        except psycopg2.DatabaseError as e:
            conn.rollback()  # Asegura que la conexión no quede en un estado erróneo
            raise RuntimeError(f"Error al ejecutar la consulta: {e}")
        except Exception as e:
            raise RuntimeError(f"Error inesperado: {e}")
        
    def get_all_data(self, query: str, data_tuple: tuple = ()):
        try:
            with self._get_connection() as conn:
                with conn.cursor() as cursor:
                    cursor.execute(query, data_tuple)
                    result = cursor.fetchall()
                    return result
        except psycopg2.DatabaseError as e:
            conn.rollback()  # Asegura que la conexión no quede en un estado erróneo
            raise RuntimeError(f"Error al ejecutar la consulta: {e}")
        except Exception as e:
            raise RuntimeError(f"Error inesperado: {e}")

    def get_all_data_dict(self, query):
        try:
            with self._get_connection() as conn:
                with conn.cursor(cursor_factory=RealDictCursor) as cursor:
                    cursor.execute(query)
                    return cursor.fetchall()
        except psycopg2.DatabaseError as e:
            raise RuntimeError(f"Error al ejecutar la consulta: {e}")
        except Exception as e:
            raise RuntimeError(f"Error inesperado: {e}")

    def update_data(self, query: str, data_tuple: tuple) -> bool:
        """
        """
        try:
            with self._get_connection() as conn:
                with conn.cursor() as cursor:
                    cursor.execute(query, data_tuple)
                    if cursor.rowcount == 0:
                        return False
                    conn.commit()  # Confirmar explícitamente la transacción
                    return True
        except Exception as e:
            conn.rollback()  # Revertir en caso de error
            raise RuntimeError(f"Error al actualizar la contraseña: {e}")

        
    def update_pswd(self, pswd: str, email: str) -> bool:
        """
        Actualiza la contraseña de un usuario en la base de datos.
        
        Args:
            pswd: Nueva contraseña (debería estar hasheada)
            email: Email del usuario a actualizar
            
        Returns:
            bool: True si la actualización fue exitosa, False si no se actualizó ningún registro
            
        Raises:
            RuntimeError: Si ocurre un error durante la operación
        """
        query = "UPDATE users SET pswd = %s, is_pswd_reseted = true WHERE email = %s;"
        try:
            with self._get_connection() as conn:
                with conn.cursor() as cursor:
                    cursor.execute(query, (pswd, email))
                    # Verificar si realmente se actualizó algún registro
                    if cursor.rowcount == 0:
                        return False
                    conn.commit()  # Confirmar explícitamente la transacción
                    return True
        except Exception as e:
            conn.rollback()  # Revertir en caso de error
            raise RuntimeError(f"Error al actualizar la contraseña: {e}")