using Dapper; using GestionIntegral.Api.Models.Usuarios; using Microsoft.Extensions.Logging; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GestionIntegral.Api.Data.Repositories.Usuarios { public class UsuarioRepository : IUsuarioRepository { private readonly DbConnectionFactory _connectionFactory; private readonly ILogger _logger; public UsuarioRepository(DbConnectionFactory connectionFactory, ILogger logger) { _connectionFactory = connectionFactory; _logger = logger; } public async Task> GetAllAsync(string? userFilter, string? nombreFilter) { var sqlBuilder = new StringBuilder("SELECT * FROM dbo.gral_Usuarios WHERE 1=1"); var parameters = new DynamicParameters(); if (!string.IsNullOrWhiteSpace(userFilter)) { sqlBuilder.Append(" AND [User] LIKE @UserParam"); parameters.Add("UserParam", $"%{userFilter}%"); } if (!string.IsNullOrWhiteSpace(nombreFilter)) { sqlBuilder.Append(" AND (Nombre LIKE @NombreParam OR Apellido LIKE @NombreParam)"); parameters.Add("NombreParam", $"%{nombreFilter}%"); } sqlBuilder.Append(" ORDER BY [User];"); try { using var connection = _connectionFactory.CreateConnection(); return await connection.QueryAsync(sqlBuilder.ToString(), parameters); } catch (Exception ex) { _logger.LogError(ex, "Error al obtener todos los Usuarios."); return Enumerable.Empty(); } } public async Task> GetAllWithProfileNameAsync(string? userFilter, string? nombreFilter) { var sqlBuilder = new StringBuilder(@" SELECT u.*, p.perfil AS NombrePerfil FROM dbo.gral_Usuarios u INNER JOIN dbo.gral_Perfiles p ON u.IdPerfil = p.id WHERE 1=1"); var parameters = new DynamicParameters(); if (!string.IsNullOrWhiteSpace(userFilter)) { sqlBuilder.Append(" AND u.[User] LIKE @UserParam"); parameters.Add("UserParam", $"%{userFilter}%"); } if (!string.IsNullOrWhiteSpace(nombreFilter)) { sqlBuilder.Append(" AND (u.Nombre LIKE @NombreParam OR u.Apellido LIKE @NombreParam)"); parameters.Add("NombreParam", $"%{nombreFilter}%"); } sqlBuilder.Append(" ORDER BY u.[User];"); try { using var connection = _connectionFactory.CreateConnection(); return await connection.QueryAsync( sqlBuilder.ToString(), (usuario, nombrePerfil) => (usuario, nombrePerfil), parameters, splitOn: "NombrePerfil" ); } catch (Exception ex) { _logger.LogError(ex, "Error al obtener todos los Usuarios con nombre de perfil."); return Enumerable.Empty<(Usuario, string)>(); } } public async Task GetByIdAsync(int id) { const string sql = "SELECT * FROM dbo.gral_Usuarios WHERE Id = @Id"; try { using var connection = _connectionFactory.CreateConnection(); return await connection.QuerySingleOrDefaultAsync(sql, new { Id = id }); } catch (Exception ex) { _logger.LogError(ex, "Error al obtener Usuario por ID: {UsuarioId}", id); return null; } } public async Task<(Usuario? Usuario, string? NombrePerfil)> GetByIdWithProfileNameAsync(int id) { const string sql = @" SELECT u.*, p.perfil AS NombrePerfil FROM dbo.gral_Usuarios u INNER JOIN dbo.gral_Perfiles p ON u.IdPerfil = p.id WHERE u.Id = @Id"; try { using var connection = _connectionFactory.CreateConnection(); var result = await connection.QueryAsync( sql, (usuario, nombrePerfil) => (usuario, nombrePerfil), new { Id = id }, splitOn: "NombrePerfil" ); return result.SingleOrDefault(); } catch (Exception ex) { _logger.LogError(ex, "Error al obtener Usuario por ID con nombre de perfil: {UsuarioId}", id); return (null, null); } } public async Task GetByUsernameAsync(string username) { // Esta es la misma que en AuthRepository, si se unifican, se puede eliminar una. const string sql = "SELECT * FROM dbo.gral_Usuarios WHERE [User] = @Username"; try { using var connection = _connectionFactory.CreateConnection(); return await connection.QuerySingleOrDefaultAsync(sql, new { Username = username }); } catch (Exception ex) { _logger.LogError(ex, "Error al obtener Usuario por Username: {Username}", username); return null; } } public async Task UserExistsAsync(string username, int? excludeId = null) { var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.gral_Usuarios WHERE [User] = @Username"); var parameters = new DynamicParameters(); parameters.Add("Username", username); if (excludeId.HasValue) { sqlBuilder.Append(" AND Id != @ExcludeId"); parameters.Add("ExcludeId", excludeId.Value); } try { using var connection = _connectionFactory.CreateConnection(); return await connection.ExecuteScalarAsync(sqlBuilder.ToString(), parameters); } catch (Exception ex) { _logger.LogError(ex, "Error en UserExistsAsync para username: {Username}", username); return true; // Asumir que existe para prevenir duplicados } } public async Task CreateAsync(Usuario nuevoUsuario, int idUsuarioCreador, IDbTransaction transaction) { const string sqlInsert = @" INSERT INTO dbo.gral_Usuarios ([User], ClaveHash, ClaveSalt, Habilitada, SupAdmin, Nombre, Apellido, IdPerfil, VerLog, DebeCambiarClave) OUTPUT INSERTED.* VALUES (@User, @ClaveHash, @ClaveSalt, @Habilitada, @SupAdmin, @Nombre, @Apellido, @IdPerfil, @VerLog, @DebeCambiarClave);"; const string sqlInsertHistorico = @" INSERT INTO dbo.gral_Usuarios_H (IdUsuario, UserNvo, HabilitadaNva, SupAdminNvo, NombreNvo, ApellidoNvo, IdPerfilNvo, DebeCambiarClaveNva, Id_UsuarioMod, FechaMod, TipoMod) VALUES (@IdUsuarioHist, @UserNvoHist, @HabilitadaNvaHist, @SupAdminNvoHist, @NombreNvoHist, @ApellidoNvoHist, @IdPerfilNvoHist, @DebeCambiarClaveNvaHist, @IdUsuarioModHist, @FechaModHist, @TipoModHist);"; var connection = transaction.Connection!; var insertedUsuario = await connection.QuerySingleAsync(sqlInsert, nuevoUsuario, transaction); if (insertedUsuario == null) throw new DataException("No se pudo crear el usuario."); await connection.ExecuteAsync(sqlInsertHistorico, new { IdUsuarioHist = insertedUsuario.Id, UserNvoHist = insertedUsuario.User, HabilitadaNvaHist = insertedUsuario.Habilitada, SupAdminNvoHist = insertedUsuario.SupAdmin, NombreNvoHist = insertedUsuario.Nombre, ApellidoNvoHist = insertedUsuario.Apellido, IdPerfilNvoHist = insertedUsuario.IdPerfil, DebeCambiarClaveNvaHist = insertedUsuario.DebeCambiarClave, IdUsuarioModHist = idUsuarioCreador, FechaModHist = DateTime.Now, TipoModHist = "Creado" }, transaction); return insertedUsuario; } public async Task UpdateAsync(Usuario usuarioAActualizar, int idUsuarioModificador, IDbTransaction transaction) { var connection = transaction.Connection!; var usuarioActual = await connection.QuerySingleOrDefaultAsync( "SELECT * FROM dbo.gral_Usuarios WHERE Id = @Id", new { usuarioAActualizar.Id }, transaction); if (usuarioActual == null) throw new KeyNotFoundException("Usuario no encontrado para actualizar."); // User (nombre de usuario) no se actualiza aquƭ const string sqlUpdate = @" UPDATE dbo.gral_Usuarios SET Habilitada = @Habilitada, SupAdmin = @SupAdmin, Nombre = @Nombre, Apellido = @Apellido, IdPerfil = @IdPerfil, VerLog = @VerLog, DebeCambiarClave = @DebeCambiarClave WHERE Id = @Id;"; const string sqlInsertHistorico = @" INSERT INTO dbo.gral_Usuarios_H (IdUsuario, UserAnt, UserNvo, HabilitadaAnt, HabilitadaNva, SupAdminAnt, SupAdminNvo, NombreAnt, NombreNvo, ApellidoAnt, ApellidoNvo, IdPerfilAnt, IdPerfilNvo, DebeCambiarClaveAnt, DebeCambiarClaveNva, Id_UsuarioMod, FechaMod, TipoMod) VALUES (@IdUsuarioHist, @UserAntHist, @UserNvoHist, @HabilitadaAntHist, @HabilitadaNvaHist, @SupAdminAntHist, @SupAdminNvoHist, @NombreAntHist, @NombreNvoHist, @ApellidoAntHist, @ApellidoNvoHist, @IdPerfilAntHist, @IdPerfilNvoHist, @DebeCambiarClaveAntHist, @DebeCambiarClaveNvaHist, @IdUsuarioModHist, @FechaModHist, @TipoModHist);"; await connection.ExecuteAsync(sqlInsertHistorico, new { IdUsuarioHist = usuarioActual.Id, UserAntHist = usuarioActual.User, UserNvoHist = usuarioAActualizar.User, // Aunque no cambiemos User, lo registramos HabilitadaAntHist = usuarioActual.Habilitada, HabilitadaNvaHist = usuarioAActualizar.Habilitada, SupAdminAntHist = usuarioActual.SupAdmin, SupAdminNvoHist = usuarioAActualizar.SupAdmin, NombreAntHist = usuarioActual.Nombre, NombreNvoHist = usuarioAActualizar.Nombre, ApellidoAntHist = usuarioActual.Apellido, ApellidoNvoHist = usuarioAActualizar.Apellido, IdPerfilAntHist = usuarioActual.IdPerfil, IdPerfilNvoHist = usuarioAActualizar.IdPerfil, DebeCambiarClaveAntHist = usuarioActual.DebeCambiarClave, DebeCambiarClaveNvaHist = usuarioAActualizar.DebeCambiarClave, IdUsuarioModHist = idUsuarioModificador, FechaModHist = DateTime.Now, TipoModHist = "Actualizado" }, transaction); var rowsAffected = await connection.ExecuteAsync(sqlUpdate, usuarioAActualizar, transaction); return rowsAffected == 1; } public async Task SetPasswordAsync(int userId, string newHash, string newSalt, bool debeCambiarClave, int idUsuarioModificador, IDbTransaction transaction) { var connection = transaction.Connection!; var usuarioActual = await connection.QuerySingleOrDefaultAsync( "SELECT * FROM dbo.gral_Usuarios WHERE Id = @Id", new { Id = userId }, transaction); if (usuarioActual == null) throw new KeyNotFoundException("Usuario no encontrado para cambiar contraseƱa."); const string sqlUpdate = @"UPDATE dbo.gral_Usuarios SET ClaveHash = @ClaveHash, ClaveSalt = @ClaveSalt, DebeCambiarClave = @DebeCambiarClave WHERE Id = @UserId"; const string sqlInsertHistorico = @" INSERT INTO dbo.gral_Usuarios_H (IdUsuario, UserNvo, HabilitadaNva, SupAdminNvo, NombreNvo, ApellidoNvo, IdPerfilNvo, DebeCambiarClaveAnt, DebeCambiarClaveNva, Id_UsuarioMod, FechaMod, TipoMod) VALUES (@IdUsuarioHist, @UserNvoHist, @HabilitadaNvaHist, @SupAdminNvoHist, @NombreNvoHist, @ApellidoNvoHist, @IdPerfilNvoHist, @DebeCambiarClaveAntHist, @DebeCambiarClaveNvaHist, @IdUsuarioModHist, @FechaModHist, @TipoModHist);"; await connection.ExecuteAsync(sqlInsertHistorico, new { IdUsuarioHist = usuarioActual.Id, UserNvoHist = usuarioActual.User, // No cambia el user HabilitadaNvaHist = usuarioActual.Habilitada, SupAdminNvoHist = usuarioActual.SupAdmin, NombreNvoHist = usuarioActual.Nombre, ApellidoNvoHist = usuarioActual.Apellido, IdPerfilNvoHist = usuarioActual.IdPerfil, DebeCambiarClaveAntHist = usuarioActual.DebeCambiarClave, // Estado anterior de este flag DebeCambiarClaveNvaHist = debeCambiarClave, // Nuevo estado de este flag IdUsuarioModHist = idUsuarioModificador, FechaModHist = DateTime.Now, TipoModHist = "Clave Cambiada" // O "Clave Reseteada" }, transaction); var rowsAffected = await connection.ExecuteAsync(sqlUpdate, new { ClaveHash = newHash, ClaveSalt = newSalt, DebeCambiarClave = debeCambiarClave, UserId = userId }, transaction); return rowsAffected == 1; } } }