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 PerfilRepository : IPerfilRepository { private readonly DbConnectionFactory _connectionFactory; private readonly ILogger _logger; public PerfilRepository(DbConnectionFactory connectionFactory, ILogger logger) { _connectionFactory = connectionFactory; _logger = logger; } public async Task> GetAllAsync(string? nombreFilter) { var sqlBuilder = new StringBuilder("SELECT id AS Id, perfil AS NombrePerfil, descPerfil AS Descripcion FROM dbo.gral_Perfiles WHERE 1=1"); var parameters = new DynamicParameters(); if (!string.IsNullOrWhiteSpace(nombreFilter)) { sqlBuilder.Append(" AND perfil LIKE @NombreFilter"); parameters.Add("NombreFilter", $"%{nombreFilter}%"); } sqlBuilder.Append(" ORDER BY perfil;"); try { using var connection = _connectionFactory.CreateConnection(); return await connection.QueryAsync(sqlBuilder.ToString(), parameters); } catch (Exception ex) { _logger.LogError(ex, "Error al obtener todos los Perfiles. Filtro: {NombreFilter}", nombreFilter); return Enumerable.Empty(); } } public async Task GetByIdAsync(int id) { const string sql = "SELECT id AS Id, perfil AS NombrePerfil, descPerfil AS Descripcion FROM dbo.gral_Perfiles 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 Perfil por ID: {IdPerfil}", id); return null; } } public async Task ExistsByNameAsync(string nombrePerfil, int? excludeId = null) { var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.gral_Perfiles WHERE perfil = @NombrePerfil"); var parameters = new DynamicParameters(); parameters.Add("NombrePerfil", nombrePerfil); if (excludeId.HasValue) { sqlBuilder.Append(" AND id != @ExcludeId"); parameters.Add("ExcludeId", excludeId.Value); } try { using var connection = _connectionFactory.CreateConnection(); var count = await connection.ExecuteScalarAsync(sqlBuilder.ToString(), parameters); return count > 0; } catch (Exception ex) { _logger.LogError(ex, "Error en ExistsByNameAsync para Perfil con nombre: {NombrePerfil}", nombrePerfil); return true; } } public async Task IsInUseAsync(int id) { const string sqlCheckUsuarios = "SELECT TOP 1 1 FROM dbo.gral_Usuarios WHERE IdPerfil = @IdPerfil"; const string sqlCheckPermisos = "SELECT TOP 1 1 FROM dbo.gral_PermisosPerfiles WHERE idPerfil = @IdPerfil"; try { using var connection = _connectionFactory.CreateConnection(); var inUsuarios = await connection.ExecuteScalarAsync(sqlCheckUsuarios, new { IdPerfil = id }); if (inUsuarios.HasValue && inUsuarios.Value == 1) return true; var inPermisos = await connection.ExecuteScalarAsync(sqlCheckPermisos, new { IdPerfil = id }); return inPermisos.HasValue && inPermisos.Value == 1; } catch (Exception ex) { _logger.LogError(ex, "Error en IsInUseAsync para Perfil ID: {IdPerfil}", id); return true; } } public async Task CreateAsync(Perfil nuevoPerfil, int idUsuario, IDbTransaction transaction) { const string sqlInsert = @" INSERT INTO dbo.gral_Perfiles (perfil, descPerfil) OUTPUT INSERTED.id AS Id, INSERTED.perfil AS NombrePerfil, INSERTED.descPerfil AS Descripcion VALUES (@NombrePerfil, @Descripcion);"; const string sqlInsertHistorico = @" INSERT INTO dbo.gral_Perfiles_H (idPerfil, perfil, descPerfil, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPerfilHist, @NombrePerfilHist, @DescripcionHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; var connection = transaction.Connection!; // La conexión debe venir de la transacción var insertedPerfil = await connection.QuerySingleAsync( sqlInsert, new { nuevoPerfil.NombrePerfil, nuevoPerfil.Descripcion }, transaction: transaction); if (insertedPerfil == null || insertedPerfil.Id <= 0) { throw new DataException("No se pudo obtener el ID del perfil insertado."); } await connection.ExecuteAsync(sqlInsertHistorico, new { IdPerfilHist = insertedPerfil.Id, // Correcto NombrePerfilHist = insertedPerfil.NombrePerfil, DescripcionHist = insertedPerfil.Descripcion, IdUsuarioHist = idUsuario, FechaModHist = DateTime.Now, TipoModHist = "Insertada" }, transaction: transaction); return insertedPerfil; } public async Task UpdateAsync(Perfil perfilAActualizar, int idUsuario, IDbTransaction transaction) { var connection = transaction.Connection!; // Obtener el estado actual PARA EL HISTORIAL DENTRO DE LA MISMA TRANSACCIÓN var perfilActual = await connection.QuerySingleOrDefaultAsync( "SELECT id AS Id, perfil AS NombrePerfil, descPerfil AS Descripcion FROM dbo.gral_Perfiles WHERE id = @Id", new { Id = perfilAActualizar.Id }, transaction); if (perfilActual == null) { // Esto no debería pasar si el servicio verifica la existencia antes, pero es una salvaguarda. throw new KeyNotFoundException($"Perfil con ID {perfilAActualizar.Id} no encontrado para actualizar."); } const string sqlUpdate = "UPDATE dbo.gral_Perfiles SET perfil = @NombrePerfil, descPerfil = @Descripcion WHERE id = @Id;"; const string sqlInsertHistorico = @" INSERT INTO dbo.gral_Perfiles_H (idPerfil, perfil, descPerfil, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPerfilHist, @NombrePerfilHist, @DescripcionHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; // Insertar en historial con los valores ANTES de la modificación await connection.ExecuteAsync(sqlInsertHistorico, new { IdPerfilHist = perfilActual.Id, NombrePerfilHist = perfilActual.NombrePerfil, DescripcionHist = perfilActual.Descripcion, IdUsuarioHist = idUsuario, FechaModHist = DateTime.Now, TipoModHist = "Modificada" }, transaction: transaction); // Actualizar la tabla principal var rowsAffected = await connection.ExecuteAsync(sqlUpdate, perfilAActualizar, transaction: transaction); return rowsAffected == 1; } public async Task DeleteAsync(int id, int idUsuario, IDbTransaction transaction) { var connection = transaction.Connection!; // Obtener el estado actual PARA EL HISTORIAL DENTRO DE LA MISMA TRANSACCIÓN var perfilActual = await connection.QuerySingleOrDefaultAsync( "SELECT id AS Id, perfil AS NombrePerfil, descPerfil AS Descripcion FROM dbo.gral_Perfiles WHERE id = @Id", new { Id = id }, transaction); if (perfilActual == null) { throw new KeyNotFoundException($"Perfil con ID {id} no encontrado para eliminar."); } const string sqlDelete = "DELETE FROM dbo.gral_Perfiles WHERE id = @Id"; const string sqlInsertHistorico = @" INSERT INTO dbo.gral_Perfiles_H (idPerfil, perfil, descPerfil, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPerfilHist, @NombrePerfilHist, @DescripcionHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; // Insertar en historial con los valores ANTES de la eliminación await connection.ExecuteAsync(sqlInsertHistorico, new { IdPerfilHist = perfilActual.Id, NombrePerfilHist = perfilActual.NombrePerfil, DescripcionHist = perfilActual.Descripcion, IdUsuarioHist = idUsuario, FechaModHist = DateTime.Now, TipoModHist = "Eliminada" }, transaction: transaction); // Eliminar de la tabla principal var rowsAffected = await connection.ExecuteAsync(sqlDelete, new { Id = id }, transaction: transaction); return rowsAffected == 1; } public async Task> GetPermisoIdsByPerfilIdAsync(int idPerfil) { const string sql = "SELECT idPermiso FROM dbo.gral_PermisosPerfiles WHERE idPerfil = @IdPerfil"; try { using var connection = _connectionFactory.CreateConnection(); return await connection.QueryAsync(sql, new { IdPerfil = idPerfil }); } catch (Exception ex) { _logger.LogError(ex, "Error al obtener IDs de permisos para el Perfil ID: {IdPerfil}", idPerfil); return Enumerable.Empty(); } } public async Task UpdatePermisosByPerfilIdAsync(int idPerfil, IEnumerable nuevosPermisosIds, IDbTransaction transaction) { var connection = transaction.Connection!; // 1. Eliminar todos los permisos existentes para este perfil (dentro de la transacción) const string sqlDelete = "DELETE FROM dbo.gral_PermisosPerfiles WHERE idPerfil = @IdPerfil"; await connection.ExecuteAsync(sqlDelete, new { IdPerfil = idPerfil }, transaction: transaction); // 2. Insertar los nuevos permisos (si hay alguno) if (nuevosPermisosIds != null && nuevosPermisosIds.Any()) { const string sqlInsert = "INSERT INTO dbo.gral_PermisosPerfiles (idPerfil, idPermiso) VALUES (@IdPerfil, @IdPermiso)"; // Dapper puede manejar una lista de objetos para inserciones múltiples var permisosParaInsertar = nuevosPermisosIds.Select(idPermiso => new { IdPerfil = idPerfil, IdPermiso = idPermiso }); await connection.ExecuteAsync(sqlInsert, permisosParaInsertar, transaction: transaction); } // No hay tabla _H para gral_PermisosPerfiles directamente en este diseño. // La auditoría de qué usuario cambió los permisos de un perfil se podría registrar // en una tabla de log de acciones más general si fuera necesario, o deducir // indirectamente si la interfaz de usuario solo permite a SuperAdmin hacer esto. } } }