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 PorcPagoRepository : IPorcPagoRepository { private readonly DbConnectionFactory _cf; // Renombrado para brevedad private readonly ILogger _log; // Renombrado para brevedad public PorcPagoRepository(DbConnectionFactory connectionFactory, ILogger logger) { _cf = connectionFactory; _log = logger; } public async Task> GetByPublicacionIdAsync(int idPublicacion) { const string sql = @" SELECT pp.Id_Porcentaje AS IdPorcentaje, pp.Id_Publicacion AS IdPublicacion, pp.Id_Distribuidor AS IdDistribuidor, pp.VigenciaD, pp.VigenciaH, pp.Porcentaje, d.Nombre AS NombreDistribuidor FROM dbo.dist_PorcPago pp INNER JOIN dbo.dist_dtDistribuidores d ON pp.Id_Distribuidor = d.Id_Distribuidor WHERE pp.Id_Publicacion = @IdPublicacionParam ORDER BY d.Nombre, pp.VigenciaD DESC"; try { using var connection = _cf.CreateConnection(); return await connection.QueryAsync( sql, (porcPago, nombreDist) => (porcPago, nombreDist), new { IdPublicacionParam = idPublicacion }, splitOn: "NombreDistribuidor" ); } catch (Exception ex) { _log.LogError(ex, "Error al obtener Porcentajes de Pago por Publicacion ID: {IdPublicacion}", idPublicacion); return Enumerable.Empty<(PorcPago, string)>(); } } public async Task GetByIdAsync(int idPorcentaje) { const string sql = @" SELECT Id_Porcentaje AS IdPorcentaje, Id_Publicacion AS IdPublicacion, Id_Distribuidor AS IdDistribuidor, VigenciaD, VigenciaH, Porcentaje FROM dbo.dist_PorcPago WHERE Id_Porcentaje = @IdPorcentajeParam"; try { using var connection = _cf.CreateConnection(); return await connection.QuerySingleOrDefaultAsync(sql, new { IdPorcentajeParam = idPorcentaje }); } catch (Exception ex) { _log.LogError(ex, "Error al obtener Porcentaje de Pago por ID: {IdPorcentaje}", idPorcentaje); return null; } } public async Task GetActiveByPublicacionDistribuidorAndDateAsync(int idPublicacion, int idDistribuidor, DateTime fecha, IDbTransaction? transaction = null) { const string sql = @" SELECT TOP 1 Id_Porcentaje AS IdPorcentaje, Id_Publicacion AS IdPublicacion, Id_Distribuidor AS IdDistribuidor, VigenciaD, VigenciaH, Porcentaje FROM dbo.dist_PorcPago WHERE Id_Publicacion = @IdPublicacionParam AND Id_Distribuidor = @IdDistribuidorParam AND VigenciaD <= @FechaParam AND (VigenciaH IS NULL OR VigenciaH >= @FechaParam) ORDER BY VigenciaD DESC;"; var cn = transaction?.Connection ?? _cf.CreateConnection(); bool ownConnection = transaction == null; PorcPago? result = null; try { if (ownConnection && cn.State == ConnectionState.Closed && cn is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); result = await cn.QuerySingleOrDefaultAsync(sql, new { IdPublicacionParam = idPublicacion, IdDistribuidorParam = idDistribuidor, FechaParam = fecha.Date }, transaction); } finally { if (ownConnection && cn.State == ConnectionState.Open && cn is System.Data.Common.DbConnection dbConnClose) await dbConnClose.CloseAsync(); if (ownConnection) (cn as IDisposable)?.Dispose(); } return result; } public async Task GetPreviousActivePorcPagoAsync(int idPublicacion, int idDistribuidor, DateTime vigenciaDNuevo, IDbTransaction transaction) { const string sql = @" SELECT TOP 1 Id_Porcentaje AS IdPorcentaje, Id_Publicacion AS IdPublicacion, Id_Distribuidor AS IdDistribuidor, VigenciaD, VigenciaH, Porcentaje FROM dbo.dist_PorcPago WHERE Id_Publicacion = @IdPublicacionParam AND Id_Distribuidor = @IdDistribuidorParam AND VigenciaD < @VigenciaDNuevoParam AND VigenciaH IS NULL ORDER BY VigenciaD DESC;"; return await transaction.Connection!.QuerySingleOrDefaultAsync(sql, new { IdPublicacionParam = idPublicacion, IdDistribuidorParam = idDistribuidor, VigenciaDNuevoParam = vigenciaDNuevo.Date }, transaction); } public async Task CreateAsync(PorcPago nuevoPorcPago, int idUsuario, IDbTransaction transaction) { const string sqlInsert = @" INSERT INTO dbo.dist_PorcPago (Id_Publicacion, Id_Distribuidor, VigenciaD, VigenciaH, Porcentaje) OUTPUT INSERTED.Id_Porcentaje AS IdPorcentaje, INSERTED.Id_Publicacion AS IdPublicacion, INSERTED.Id_Distribuidor AS IdDistribuidor, INSERTED.VigenciaD, INSERTED.VigenciaH, INSERTED.Porcentaje VALUES (@IdPublicacion, @IdDistribuidor, @VigenciaD, @VigenciaH, @Porcentaje);"; const string sqlInsertHistorico = @" INSERT INTO dbo.dist_PorcPago_H (Id_Porcentaje, Id_Publicacion, Id_Distribuidor, VigenciaD, VigenciaH, Porcentaje, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPorcentajeHist, @IdPublicacionHist, @IdDistribuidorHist, @VigenciaDHist, @VigenciaHHist, @PorcentajeHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; var inserted = await transaction.Connection!.QuerySingleAsync(sqlInsert, nuevoPorcPago, transaction); if (inserted == null || inserted.IdPorcentaje == 0) throw new DataException("Error al crear porcentaje de pago o al obtener ID."); await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { IdPorcentajeHist = inserted.IdPorcentaje, IdPublicacionHist = inserted.IdPublicacion, IdDistribuidorHist = inserted.IdDistribuidor, VigenciaDHist = inserted.VigenciaD, VigenciaHHist = inserted.VigenciaH, PorcentajeHist = inserted.Porcentaje, IdUsuarioHist = idUsuario, FechaModHist = DateTime.Now, TipoModHist = "Creado" }, transaction); return inserted; } public async Task UpdateAsync(PorcPago porcPagoAActualizar, int idUsuario, IDbTransaction transaction) { var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( @"SELECT Id_Porcentaje AS IdPorcentaje, Id_Publicacion AS IdPublicacion, Id_Distribuidor AS IdDistribuidor, VigenciaD, VigenciaH, Porcentaje FROM dbo.dist_PorcPago WHERE Id_Porcentaje = @IdPorcentajeParam", new { IdPorcentajeParam = porcPagoAActualizar.IdPorcentaje }, transaction); if (actual == null) throw new KeyNotFoundException("Porcentaje de pago no encontrado."); const string sqlUpdate = @" UPDATE dbo.dist_PorcPago SET Porcentaje = @Porcentaje, VigenciaH = @VigenciaH WHERE Id_Porcentaje = @IdPorcentaje;"; const string sqlInsertHistorico = @" INSERT INTO dbo.dist_PorcPago_H (Id_Porcentaje, Id_Publicacion, Id_Distribuidor, VigenciaD, VigenciaH, Porcentaje, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPorcentajeHist, @IdPublicacionHist, @IdDistribuidorHist, @VigenciaDHist, @VigenciaHHist, @PorcentajeHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { IdPorcentajeHist = actual.IdPorcentaje, IdPublicacionHist = actual.IdPublicacion, IdDistribuidorHist = actual.IdDistribuidor, VigenciaDHist = actual.VigenciaD, VigenciaHHist = actual.VigenciaH, // Valor ANTERIOR de VigenciaH PorcentajeHist = actual.Porcentaje, // Valor ANTERIOR de Porcentaje IdUsuarioHist = idUsuario, FechaModHist = DateTime.Now, TipoModHist = "Actualizado" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, porcPagoAActualizar, transaction); return rowsAffected == 1; } public async Task DeleteAsync(int idPorcentaje, int idUsuario, IDbTransaction transaction) { var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( @"SELECT Id_Porcentaje AS IdPorcentaje, Id_Publicacion AS IdPublicacion, Id_Distribuidor AS IdDistribuidor, VigenciaD, VigenciaH, Porcentaje FROM dbo.dist_PorcPago WHERE Id_Porcentaje = @IdPorcentajeParam", new { IdPorcentajeParam = idPorcentaje }, transaction); if (actual == null) throw new KeyNotFoundException("Porcentaje de pago no encontrado para eliminar."); const string sqlDelete = "DELETE FROM dbo.dist_PorcPago WHERE Id_Porcentaje = @IdPorcentajeParam"; const string sqlInsertHistorico = @" INSERT INTO dbo.dist_PorcPago_H (Id_Porcentaje, Id_Publicacion, Id_Distribuidor, VigenciaD, VigenciaH, Porcentaje, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPorcentajeHist, @IdPublicacionHist, @IdDistribuidorHist, @VigenciaDHist, @VigenciaHHist, @PorcentajeHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { IdPorcentajeHist = actual.IdPorcentaje, IdPublicacionHist = actual.IdPublicacion, IdDistribuidorHist = actual.IdDistribuidor, VigenciaDHist = actual.VigenciaD, VigenciaHHist = actual.VigenciaH, PorcentajeHist = actual.Porcentaje, IdUsuarioHist = idUsuario, FechaModHist = DateTime.Now, TipoModHist = "Eliminado" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdPorcentajeParam = idPorcentaje }, transaction); return rowsAffected == 1; } public async Task DeleteByPublicacionIdAsync(int idPublicacion, int idUsuarioAuditoria, IDbTransaction transaction) { const string selectSql = @" SELECT Id_Porcentaje AS IdPorcentaje, Id_Publicacion AS IdPublicacion, Id_Distribuidor AS IdDistribuidor, VigenciaD, VigenciaH, Porcentaje FROM dbo.dist_PorcPago 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_PorcPago_H (Id_Porcentaje, Id_Publicacion, Id_Distribuidor, VigenciaD, VigenciaH, Porcentaje, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPorcentajeHist, @IdPublicacionHist, @IdDistribuidorHist, @VigenciaDHist, @VigenciaHHist, @PorcentajeHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; foreach (var item in itemsToDelete) { await transaction.Connection!.ExecuteAsync(insertHistoricoSql, new { IdPorcentajeHist = item.IdPorcentaje, IdPublicacionHist = item.IdPublicacion, IdDistribuidorHist = item.IdDistribuidor, VigenciaDHist = item.VigenciaD, VigenciaHHist = item.VigenciaH, PorcentajeHist = item.Porcentaje, IdUsuarioHist = idUsuarioAuditoria, FechaModHist = DateTime.Now, TipoModHist = "Eliminado (Cascada)" }, transaction); } } const string deleteSql = "DELETE FROM dbo.dist_PorcPago 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 PorcPago por IdPublicacion: {idPublicacion}", idPublicacion); throw; } } public async Task> GetHistorialAsync( DateTime? fechaDesde, DateTime? fechaHasta, int? idUsuarioModifico, string? tipoModificacion, int? idPorcentajeOriginal, int? idPublicacionOriginal, int? idDistribuidorOriginal) { using var connection = _cf.CreateConnection(); var sqlBuilder = new StringBuilder(@" SELECT h.Id_Porcentaje, h.Id_Publicacion, h.Id_Distribuidor, h.VigenciaD, h.VigenciaH, h.Porcentaje, h.Id_Usuario, h.FechaMod, h.TipoMod, u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico FROM dbo.dist_PorcPago_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 (idPorcentajeOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Porcentaje = @IdPorcentajeOriginalParam"); parameters.Add("IdPorcentajeOriginalParam", idPorcentajeOriginal.Value); } if (idPublicacionOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Publicacion = @IdPublicacionOriginalParam"); parameters.Add("IdPublicacionOriginalParam", idPublicacionOriginal.Value); } if (idDistribuidorOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Distribuidor = @IdDistribuidorOriginalParam"); parameters.Add("IdDistribuidorOriginalParam", idDistribuidorOriginal.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 Porcentajes de Pago (Distribuidores)."); return Enumerable.Empty<(PorcPagoHistorico, string)>(); } } } }