using Dapper; using GestionIntegral.Api.Models.Distribucion; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GestionIntegral.Api.Data.Repositories.Distribucion { public class PubliSeccionRepository : IPubliSeccionRepository { private readonly DbConnectionFactory _cf; private readonly ILogger _log; public PubliSeccionRepository(DbConnectionFactory connectionFactory, ILogger logger) { _cf = connectionFactory; _log = logger; } public async Task> GetByPublicacionIdAsync(int idPublicacion, bool? soloActivas = null) { var sqlBuilder = new StringBuilder(@" SELECT Id_Seccion AS IdSeccion, Id_Publicacion AS IdPublicacion, Nombre, Estado FROM dbo.dist_dtPubliSecciones WHERE Id_Publicacion = @IdPublicacionParam"); if (soloActivas.HasValue) { sqlBuilder.Append(soloActivas.Value ? " AND Estado = 1" : " AND Estado = 0"); } sqlBuilder.Append(" ORDER BY Nombre;"); try { using var connection = _cf.CreateConnection(); return await connection.QueryAsync(sqlBuilder.ToString(), new { IdPublicacionParam = idPublicacion }); } catch (Exception ex) { _log.LogError(ex, "Error al obtener Secciones por Publicacion ID: {IdPublicacion}", idPublicacion); return Enumerable.Empty(); } } public async Task GetByIdAsync(int idSeccion) { const string sql = @" SELECT Id_Seccion AS IdSeccion, Id_Publicacion AS IdPublicacion, Nombre, Estado FROM dbo.dist_dtPubliSecciones WHERE Id_Seccion = @IdSeccionParam"; try { using var connection = _cf.CreateConnection(); return await connection.QuerySingleOrDefaultAsync(sql, new { IdSeccionParam = idSeccion }); } catch (Exception ex) { _log.LogError(ex, "Error al obtener Sección por ID: {IdSeccion}", idSeccion); return null; } } public async Task ExistsByNameInPublicacionAsync(string nombre, int idPublicacion, int? excludeIdSeccion = null) { var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.dist_dtPubliSecciones WHERE Nombre = @NombreParam AND Id_Publicacion = @IdPublicacionParam"); var parameters = new DynamicParameters(); parameters.Add("NombreParam", nombre); parameters.Add("IdPublicacionParam", idPublicacion); if (excludeIdSeccion.HasValue) { sqlBuilder.Append(" AND Id_Seccion != @ExcludeIdSeccionParam"); parameters.Add("ExcludeIdSeccionParam", excludeIdSeccion.Value); } try { using var connection = _cf.CreateConnection(); return await connection.ExecuteScalarAsync(sqlBuilder.ToString(), parameters); } catch (Exception ex) { _log.LogError(ex, "Error en ExistsByNameInPublicacionAsync. Nombre: {Nombre}, PubID: {IdPublicacion}", nombre, idPublicacion); return true; } } public async Task IsInUseAsync(int idSeccion) { // Verificar en bob_RegPublicaciones y bob_StockBobinas (si Id_Seccion se usa allí) using var connection = _cf.CreateConnection(); string[] checkQueries = { "SELECT TOP 1 1 FROM dbo.bob_RegPublicaciones WHERE Id_Seccion = @IdSeccionParam", "SELECT TOP 1 1 FROM dbo.bob_StockBobinas WHERE Id_Seccion = @IdSeccionParam" }; try { foreach (var query in checkQueries) { if (await connection.ExecuteScalarAsync(query, new { IdSeccionParam = idSeccion }) == 1) return true; } return false; } catch (Exception ex) { _log.LogError(ex, "Error en IsInUseAsync para PubliSeccion ID: {idSeccion}", idSeccion); return true; } } public async Task CreateAsync(PubliSeccion nuevaSeccion, int idUsuario, IDbTransaction transaction) { const string sqlInsert = @" INSERT INTO dbo.dist_dtPubliSecciones (Id_Publicacion, Nombre, Estado) OUTPUT INSERTED.Id_Seccion AS IdSeccion, INSERTED.Id_Publicacion AS IdPublicacion, INSERTED.Nombre, INSERTED.Estado VALUES (@IdPublicacion, @Nombre, @Estado);"; const string sqlInsertHistorico = @" INSERT INTO dbo.dist_dtPubliSecciones_H (Id_Seccion, Id_Publicacion, Nombre, Estado, Id_Usuario, FechaMod, TipoMod) VALUES (@IdSeccionParam, @IdPublicacionParam, @NombreParam, @EstadoParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; var inserted = await transaction.Connection!.QuerySingleAsync(sqlInsert, nuevaSeccion, transaction); if (inserted == null || inserted.IdSeccion == 0) throw new DataException("Error al crear sección o ID no generado."); await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { IdSeccionParam = inserted.IdSeccion, IdPublicacionParam = inserted.IdPublicacion, NombreParam = inserted.Nombre, EstadoParam = inserted.Estado, IdUsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Creada" }, transaction); return inserted; } public async Task UpdateAsync(PubliSeccion seccionAActualizar, int idUsuario, IDbTransaction transaction) { var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( @"SELECT Id_Seccion AS IdSeccion, Id_Publicacion AS IdPublicacion, Nombre, Estado FROM dbo.dist_dtPubliSecciones WHERE Id_Seccion = @IdSeccionParam", new { IdSeccionParam = seccionAActualizar.IdSeccion }, transaction); if (actual == null) throw new KeyNotFoundException("Sección no encontrada."); const string sqlUpdate = @" UPDATE dbo.dist_dtPubliSecciones SET Nombre = @Nombre, Estado = @Estado WHERE Id_Seccion = @IdSeccion;"; const string sqlInsertHistorico = @" INSERT INTO dbo.dist_dtPubliSecciones_H (Id_Seccion, Id_Publicacion, Nombre, Estado, Id_Usuario, FechaMod, TipoMod) VALUES (@IdSeccionParam, @IdPublicacionParam, @NombreParam, @EstadoParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { IdSeccionParam = actual.IdSeccion, IdPublicacionParam = actual.IdPublicacion, // Tomar de 'actual' ya que no se modifica NombreParam = actual.Nombre, // Valor ANTERIOR EstadoParam = actual.Estado, // Valor ANTERIOR IdUsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Actualizada" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, seccionAActualizar, transaction); return rowsAffected == 1; } public async Task> GetByIdsAndPublicacionAsync(IEnumerable idsSeccion, int idPublicacion, bool? soloActivas = null) { if (idsSeccion == null || !idsSeccion.Any()) { return Enumerable.Empty(); } var sqlBuilder = new StringBuilder(@" SELECT Id_Seccion AS IdSeccion, Id_Publicacion AS IdPublicacion, Nombre, Estado FROM dbo.dist_dtPubliSecciones WHERE Id_Publicacion = @IdPublicacionParam AND Id_Seccion IN @IdsSeccionParam"); var parameters = new DynamicParameters(); parameters.Add("IdPublicacionParam", idPublicacion); parameters.Add("IdsSeccionParam", idsSeccion); if (soloActivas.HasValue) { sqlBuilder.Append(" AND Estado = @EstadoParam"); parameters.Add("EstadoParam", soloActivas.Value); } using var connection = _cf.CreateConnection(); return await connection.QueryAsync(sqlBuilder.ToString(), parameters); } public async Task DeleteAsync(int idSeccion, int idUsuario, IDbTransaction transaction) { var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( @"SELECT Id_Seccion AS IdSeccion, Id_Publicacion AS IdPublicacion, Nombre, Estado FROM dbo.dist_dtPubliSecciones WHERE Id_Seccion = @IdSeccionParam", new { IdSeccionParam = idSeccion }, transaction); if (actual == null) throw new KeyNotFoundException("Sección no encontrada para eliminar."); const string sqlDelete = "DELETE FROM dbo.dist_dtPubliSecciones WHERE Id_Seccion = @IdSeccionParam"; const string sqlInsertHistorico = @" INSERT INTO dbo.dist_dtPubliSecciones_H (Id_Seccion, Id_Publicacion, Nombre, Estado, Id_Usuario, FechaMod, TipoMod) VALUES (@IdSeccionParam, @IdPublicacionParam, @NombreParam, @EstadoParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { IdSeccionParam = actual.IdSeccion, IdPublicacionParam = actual.IdPublicacion, NombreParam = actual.Nombre, EstadoParam = actual.Estado, IdUsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Eliminada" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdSeccionParam = idSeccion }, transaction); return rowsAffected == 1; } public async Task DeleteByPublicacionIdAsync(int idPublicacion, int idUsuarioAuditoria, IDbTransaction transaction) { const string selectSql = @" SELECT Id_Seccion AS IdSeccion, Id_Publicacion AS IdPublicacion, Nombre, Estado FROM dbo.dist_dtPubliSecciones WHERE Id_Publicacion = @IdPublicacionParam"; var itemsToDelete = await transaction.Connection!.QueryAsync(selectSql, new { IdPublicacionParam = idPublicacion }, transaction); if (itemsToDelete.Any()) { const string insertHistoricoSql = @" INSERT INTO dbo.dist_dtPubliSecciones_H (Id_Seccion, Id_Publicacion, Nombre, Estado, Id_Usuario, FechaMod, TipoMod) VALUES (@IdSeccionParam, @IdPublicacionParam, @NombreParam, @EstadoParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; foreach (var item in itemsToDelete) { await transaction.Connection!.ExecuteAsync(insertHistoricoSql, new { IdSeccionParam = item.IdSeccion, IdPublicacionParam = item.IdPublicacion, NombreParam = item.Nombre, EstadoParam = item.Estado, IdUsuarioParam = idUsuarioAuditoria, FechaModParam = DateTime.Now, TipoModParam = "Eliminado (Cascada)" }, transaction); } } const string deleteSql = "DELETE FROM dbo.dist_dtPubliSecciones WHERE Id_Publicacion = @IdPublicacionParam"; try { await transaction.Connection!.ExecuteAsync(deleteSql, new { IdPublicacionParam = idPublicacion }, transaction: transaction); return true; } catch (System.Exception ex) { _log.LogError(ex, "Error al eliminar PubliSecciones por IdPublicacion: {idPublicacion}", idPublicacion); throw; } } public async Task> GetHistorialAsync( DateTime? fechaDesde, DateTime? fechaHasta, int? idUsuarioModifico, string? tipoModificacion, int? idSeccionOriginal, int? idPublicacionOriginal) { using var connection = _cf.CreateConnection(); var sqlBuilder = new StringBuilder(@" SELECT h.Id_Seccion, h.Id_Publicacion, h.Nombre, h.Estado, h.Id_Usuario, h.FechaMod, h.TipoMod, u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico FROM dbo.dist_dtPubliSecciones_H h JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id WHERE 1=1"); var parameters = new DynamicParameters(); if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } if (idSeccionOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Seccion = @IdSeccionOriginalParam"); parameters.Add("IdSeccionOriginalParam", idSeccionOriginal.Value); } if (idPublicacionOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Publicacion = @IdPublicacionOriginalParam"); parameters.Add("IdPublicacionOriginalParam", idPublicacionOriginal.Value); } sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); try { var result = await connection.QueryAsync( sqlBuilder.ToString(), (hist, userName) => (hist, userName), parameters, splitOn: "NombreUsuarioModifico" ); return result; } catch (Exception ex) { _log.LogError(ex, "Error al obtener historial de Secciones de Publicación."); return Enumerable.Empty<(PubliSeccionHistorico, string)>(); } } } }