using Dapper; using GestionIntegral.Api.Models.Distribucion; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Data; using System.Linq; // Necesario para .Any() using System.Threading.Tasks; namespace GestionIntegral.Api.Data.Repositories.Distribucion { public class RecargoZonaRepository : IRecargoZonaRepository { private readonly DbConnectionFactory _connectionFactory; private readonly ILogger _logger; public RecargoZonaRepository(DbConnectionFactory connectionFactory, ILogger logger) { _connectionFactory = connectionFactory; _logger = logger; } public async Task> GetByPublicacionIdAsync(int idPublicacion) { const string sql = @" SELECT rz.Id_Recargo AS IdRecargo, rz.Id_Publicacion AS IdPublicacion, rz.Id_Zona AS IdZona, rz.VigenciaD, rz.VigenciaH, rz.Valor, z.Nombre AS NombreZona FROM dbo.dist_RecargoZona rz INNER JOIN dbo.dist_dtZonas z ON rz.Id_Zona = z.Id_Zona WHERE rz.Id_Publicacion = @IdPublicacionParam ORDER BY z.Nombre, rz.VigenciaD DESC"; try { using var connection = _connectionFactory.CreateConnection(); return await connection.QueryAsync( sql, (recargo, nombreZona) => (recargo, nombreZona), new { IdPublicacionParam = idPublicacion }, splitOn: "NombreZona" ); } catch (Exception ex) { _logger.LogError(ex, "Error al obtener Recargos por Zona para Publicacion ID: {IdPublicacion}", idPublicacion); return Enumerable.Empty<(RecargoZona, string)>(); } } public async Task GetByIdAsync(int idRecargo) { const string sql = @" SELECT Id_Recargo AS IdRecargo, Id_Publicacion AS IdPublicacion, Id_Zona AS IdZona, VigenciaD, VigenciaH, Valor FROM dbo.dist_RecargoZona WHERE Id_Recargo = @IdRecargoParam"; try { using var connection = _connectionFactory.CreateConnection(); return await connection.QuerySingleOrDefaultAsync(sql, new { IdRecargoParam = idRecargo }); } catch (Exception ex) { _logger.LogError(ex, "Error al obtener Recargo por Zona por ID: {IdRecargo}", idRecargo); return null; } } public async Task GetActiveByPublicacionZonaAndDateAsync(int idPublicacion, int idZona, DateTime fecha, IDbTransaction? transaction = null) { const string sql = @" SELECT TOP 1 Id_Recargo AS IdRecargo, Id_Publicacion AS IdPublicacion, Id_Zona AS IdZona, VigenciaD, VigenciaH, Valor FROM dbo.dist_RecargoZona WHERE Id_Publicacion = @IdPublicacionParam AND Id_Zona = @IdZonaParam AND VigenciaD <= @FechaParam AND (VigenciaH IS NULL OR VigenciaH >= @FechaParam) ORDER BY VigenciaD DESC;"; var cn = transaction?.Connection ?? _connectionFactory.CreateConnection(); bool ownConnection = transaction == null; RecargoZona? 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, IdZonaParam = idZona, FechaParam = fecha.Date }, transaction); } catch (Exception ex) { _logger.LogError(ex, "Error en GetActiveByPublicacionZonaAndDateAsync. IdPublicacion: {IdPublicacion}, IdZona: {IdZona}, Fecha: {Fecha}", idPublicacion, idZona, fecha); throw; } 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 GetPreviousActiveRecargoAsync(int idPublicacion, int idZona, DateTime vigenciaDNuevo, IDbTransaction transaction) { const string sql = @" SELECT TOP 1 Id_Recargo AS IdRecargo, Id_Publicacion AS IdPublicacion, Id_Zona AS IdZona, VigenciaD, VigenciaH, Valor FROM dbo.dist_RecargoZona WHERE Id_Publicacion = @IdPublicacionParam AND Id_Zona = @IdZonaParam AND VigenciaD < @VigenciaDNuevoParam AND VigenciaH IS NULL ORDER BY VigenciaD DESC;"; return await transaction.Connection!.QuerySingleOrDefaultAsync(sql, new { IdPublicacionParam = idPublicacion, IdZonaParam = idZona, VigenciaDNuevoParam = vigenciaDNuevo.Date }, transaction); } public async Task CreateAsync(RecargoZona nuevoRecargo, int idUsuario, IDbTransaction transaction) { const string sqlInsert = @" INSERT INTO dbo.dist_RecargoZona (Id_Publicacion, Id_Zona, VigenciaD, VigenciaH, Valor) OUTPUT INSERTED.Id_Recargo AS IdRecargo, INSERTED.Id_Publicacion AS IdPublicacion, INSERTED.Id_Zona AS IdZona, INSERTED.VigenciaD, INSERTED.VigenciaH, INSERTED.Valor VALUES (@IdPublicacion, @IdZona, @VigenciaD, @VigenciaH, @Valor);"; const string sqlInsertHistorico = @" INSERT INTO dbo.dist_RecargoZona_H (Id_Recargo, Id_Publicacion, Id_Zona, VigenciaD, VigenciaH, Valor, Id_Usuario, FechaMod, TipoMod) VALUES (@IdRecargoParam, @IdPublicacionParam, @IdZonaParam, @VigenciaDParam, @VigenciaHParam, @ValorParam, @Id_UsuarioParam, @FechaModParam, @TipoModParam);"; var inserted = await transaction.Connection!.QuerySingleAsync(sqlInsert, nuevoRecargo, transaction); if (inserted == null || inserted.IdRecargo == 0) throw new DataException("Error al crear recargo por zona o al obtener el ID generado."); await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { IdRecargoParam = inserted.IdRecargo, IdPublicacionParam = inserted.IdPublicacion, IdZonaParam = inserted.IdZona, VigenciaDParam = inserted.VigenciaD, VigenciaHParam = inserted.VigenciaH, ValorParam = inserted.Valor, Id_UsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Creado" }, transaction); return inserted; } public async Task UpdateAsync(RecargoZona recargoAActualizar, int idUsuario, IDbTransaction transaction) { var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( @"SELECT Id_Recargo AS IdRecargo, Id_Publicacion AS IdPublicacion, Id_Zona AS IdZona, VigenciaD, VigenciaH, Valor FROM dbo.dist_RecargoZona WHERE Id_Recargo = @IdRecargoParam", // Usar parĂ¡metro con alias new { IdRecargoParam = recargoAActualizar.IdRecargo }, transaction); if (actual == null) throw new KeyNotFoundException("Recargo por zona no encontrado."); const string sqlUpdate = @" UPDATE dbo.dist_RecargoZona SET Valor = @Valor, VigenciaH = @VigenciaH WHERE Id_Recargo = @IdRecargo;"; const string sqlInsertHistorico = @" INSERT INTO dbo.dist_RecargoZona_H (Id_Recargo, Id_Publicacion, Id_Zona, VigenciaD, VigenciaH, Valor, Id_Usuario, FechaMod, TipoMod) VALUES (@IdRecargoParam, @IdPublicacionParam, @IdZonaParam, @VigenciaDParam, @VigenciaHParam, @ValorParam, @Id_UsuarioParam, @FechaModParam, @TipoModParam);"; await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { IdRecargoParam = actual.IdRecargo, IdPublicacionParam = actual.IdPublicacion, IdZonaParam = actual.IdZona, VigenciaDParam = actual.VigenciaD, VigenciaHParam = actual.VigenciaH, ValorParam = actual.Valor, Id_UsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Actualizado" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, recargoAActualizar, transaction); return rowsAffected == 1; } public async Task DeleteAsync(int idRecargo, int idUsuario, IDbTransaction transaction) { var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( @"SELECT Id_Recargo AS IdRecargo, Id_Publicacion AS IdPublicacion, Id_Zona AS IdZona, VigenciaD, VigenciaH, Valor FROM dbo.dist_RecargoZona WHERE Id_Recargo = @IdRecargoParam", new { IdRecargoParam = idRecargo }, transaction); if (actual == null) throw new KeyNotFoundException("Recargo por zona no encontrado para eliminar."); const string sqlDelete = "DELETE FROM dbo.dist_RecargoZona WHERE Id_Recargo = @IdRecargoParam"; // Usar parĂ¡metro con alias const string sqlInsertHistorico = @" INSERT INTO dbo.dist_RecargoZona_H (Id_Recargo, Id_Publicacion, Id_Zona, VigenciaD, VigenciaH, Valor, Id_Usuario, FechaMod, TipoMod) VALUES (@IdRecargoParam, @IdPublicacionParam, @IdZonaParam, @VigenciaDParam, @VigenciaHParam, @ValorParam, @Id_UsuarioParam, @FechaModParam, @TipoModParam);"; await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { IdRecargoParam = actual.IdRecargo, IdPublicacionParam = actual.IdPublicacion, IdZonaParam = actual.IdZona, VigenciaDParam = actual.VigenciaD, VigenciaHParam = actual.VigenciaH, ValorParam = actual.Valor, Id_UsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Eliminado" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdRecargoParam = idRecargo }, transaction); return rowsAffected == 1; } public async Task DeleteByPublicacionIdAsync(int idPublicacion, int idUsuarioAuditoria, IDbTransaction transaction) { const string selectSql = @" SELECT Id_Recargo AS IdRecargo, Id_Publicacion AS IdPublicacion, Id_Zona AS IdZona, VigenciaD, VigenciaH, Valor FROM dbo.dist_RecargoZona 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_RecargoZona_H (Id_Recargo, Id_Publicacion, Id_Zona, VigenciaD, VigenciaH, Valor, Id_Usuario, FechaMod, TipoMod) VALUES (@IdRecargoParam, @IdPublicacionParam, @IdZonaParam, @VigenciaDParam, @VigenciaHParam, @ValorParam, @Id_UsuarioParam, @FechaModParam, @TipoModParam);"; foreach (var item in itemsToDelete) { await transaction.Connection!.ExecuteAsync(insertHistoricoSql, new { IdRecargoParam = item.IdRecargo, IdPublicacionParam = item.IdPublicacion, IdZonaParam = item.IdZona, VigenciaDParam = item.VigenciaD, VigenciaHParam = item.VigenciaH, ValorParam = item.Valor, Id_UsuarioParam = idUsuarioAuditoria, FechaModParam = DateTime.Now, TipoModParam = "Eliminado (Cascada)" }, transaction); } } const string deleteSql = "DELETE FROM dbo.dist_RecargoZona WHERE Id_Publicacion = @IdPublicacionParam"; try { await transaction.Connection!.ExecuteAsync(deleteSql, new { IdPublicacionParam = idPublicacion }, transaction: transaction); return true; } catch (System.Exception ex) { _logger.LogError(ex, "Error al eliminar RecargosZona por IdPublicacion: {IdPublicacion}", idPublicacion); throw; } } } }