using Dapper; using GestionIntegral.Api.Models.Distribucion; using Microsoft.Extensions.Logging; using System.Collections.Generic; using System.Data; using System.Linq; using System.Threading.Tasks; namespace GestionIntegral.Api.Data.Repositories.Distribucion { public class PrecioRepository : IPrecioRepository { private readonly DbConnectionFactory _connectionFactory; private readonly ILogger _logger; public PrecioRepository(DbConnectionFactory connectionFactory, ILogger logger) { _connectionFactory = connectionFactory; _logger = logger; } public async Task> GetByPublicacionIdAsync(int idPublicacion) { const string sql = "SELECT * FROM dbo.dist_Precios WHERE Id_Publicacion = @IdPublicacion ORDER BY VigenciaD DESC"; using var connection = _connectionFactory.CreateConnection(); return await connection.QueryAsync(sql, new { IdPublicacion = idPublicacion }); } public async Task GetByIdAsync(int idPrecio) { const string sql = "SELECT * FROM dbo.dist_Precios WHERE Id_Precio = @IdPrecio"; using var connection = _connectionFactory.CreateConnection(); return await connection.QuerySingleOrDefaultAsync(sql, new { IdPrecio = idPrecio }); } public async Task GetActiveByPublicacionAndDateAsync(int idPublicacion, DateTime fecha, IDbTransaction? transaction = null) { // Obtiene el precio vigente para una publicación en una fecha específica const string sql = @" SELECT TOP 1 * FROM dbo.dist_Precios WHERE Id_Publicacion = @IdPublicacion AND VigenciaD <= @Fecha AND (VigenciaH IS NULL OR VigenciaH >= @Fecha) ORDER BY VigenciaD DESC;"; // Por si hay solapamientos incorrectos, tomar el más reciente var cn = transaction?.Connection ?? _connectionFactory.CreateConnection(); return await cn.QuerySingleOrDefaultAsync(sql, new { IdPublicacion = idPublicacion, Fecha = fecha }, transaction); } public async Task GetPreviousActivePriceAsync(int idPublicacion, DateTime vigenciaDNuevo, IDbTransaction transaction) { // Busca el último precio activo antes de la vigenciaD del nuevo precio, que no tenga VigenciaH o cuya VigenciaH sea mayor o igual a la nueva VigenciaD - 1 día // y que no sea el mismo periodo que se está por crear/actualizar (si tuviera un ID). const string sql = @" SELECT TOP 1 * FROM dbo.dist_Precios WHERE Id_Publicacion = @IdPublicacion AND VigenciaD < @VigenciaDNuevo AND VigenciaH IS NULL ORDER BY VigenciaD DESC;"; return await transaction.Connection!.QuerySingleOrDefaultAsync(sql, new { IdPublicacion = idPublicacion, VigenciaDNuevo = vigenciaDNuevo }, transaction); } public async Task CreateAsync(Precio nuevoPrecio, int idUsuario, IDbTransaction transaction) { const string sqlInsert = @" INSERT INTO dbo.dist_Precios (Id_Publicacion, VigenciaD, VigenciaH, Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo) OUTPUT INSERTED.* VALUES (@IdPublicacion, @VigenciaD, @VigenciaH, @Lunes, @Martes, @Miercoles, @Jueves, @Viernes, @Sabado, @Domingo);"; const string sqlInsertHistorico = @" INSERT INTO dbo.dist_Precios_H (Id_Precio, Id_Publicacion, VigenciaD, VigenciaH, Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPrecio, @IdPublicacion, @VigenciaD, @VigenciaH, @Lunes, @Martes, @Miercoles, @Jueves, @Viernes, @Sabado, @Domingo, @Id_Usuario, @FechaMod, @TipoMod);"; var inserted = await transaction.Connection!.QuerySingleAsync(sqlInsert, nuevoPrecio, transaction); if (inserted == null) throw new DataException("Error al crear precio."); await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { inserted.IdPrecio, inserted.IdPublicacion, inserted.VigenciaD, inserted.VigenciaH, inserted.Lunes, inserted.Martes, inserted.Miercoles, inserted.Jueves, inserted.Viernes, inserted.Sabado, inserted.Domingo, Id_Usuario = idUsuario, FechaMod = DateTime.Now, TipoMod = "Creado" }, transaction); return inserted; } public async Task UpdateAsync(Precio precioAActualizar, int idUsuario, IDbTransaction transaction) { var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( "SELECT * FROM dbo.dist_Precios WHERE Id_Precio = @IdPrecio", new { precioAActualizar.IdPrecio }, transaction); if (actual == null) throw new KeyNotFoundException("Precio no encontrado."); const string sqlUpdate = @" UPDATE dbo.dist_Precios SET VigenciaH = @VigenciaH, Lunes = @Lunes, Martes = @Martes, Miercoles = @Miercoles, Jueves = @Jueves, Viernes = @Viernes, Sabado = @Sabado, Domingo = @Domingo -- No se permite cambiar IdPublicacion ni VigenciaD de un registro de precio existente WHERE Id_Precio = @IdPrecio;"; const string sqlInsertHistorico = @" INSERT INTO dbo.dist_Precios_H (Id_Precio, Id_Publicacion, VigenciaD, VigenciaH, Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPrecio, @IdPublicacion, @VigenciaD, @VigenciaH, @Lunes, @Martes, @Miercoles, @Jueves, @Viernes, @Sabado, @Domingo, @Id_Usuario, @FechaMod, @TipoMod);"; await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { actual.IdPrecio, actual.IdPublicacion, actual.VigenciaD, // VigenciaD actual para el historial VigenciaH = actual.VigenciaH, // VigenciaH actual para el historial Lunes = actual.Lunes, Martes = actual.Martes, Miercoles = actual.Miercoles, Jueves = actual.Jueves, Viernes = actual.Viernes, Sabado = actual.Sabado, Domingo = actual.Domingo, // Precios actuales para el historial Id_Usuario = idUsuario, FechaMod = DateTime.Now, TipoMod = "Actualizado" // O "Cerrado" si solo se actualiza VigenciaH }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, precioAActualizar, transaction); return rowsAffected == 1; } public async Task DeleteAsync(int idPrecio, int idUsuario, IDbTransaction transaction) { var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( "SELECT * FROM dbo.dist_Precios WHERE Id_Precio = @IdPrecio", new { IdPrecio = idPrecio }, transaction); if (actual == null) throw new KeyNotFoundException("Precio no encontrado para eliminar."); const string sqlDelete = "DELETE FROM dbo.dist_Precios WHERE Id_Precio = @IdPrecio"; const string sqlInsertHistorico = @" INSERT INTO dbo.dist_Precios_H (Id_Precio, Id_Publicacion, VigenciaD, VigenciaH, Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPrecio, @IdPublicacion, @VigenciaD, @VigenciaH, @Lunes, @Martes, @Miercoles, @Jueves, @Viernes, @Sabado, @Domingo, @Id_Usuario, @FechaMod, @TipoMod);"; await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { actual.IdPrecio, actual.IdPublicacion, actual.VigenciaD, actual.VigenciaH, actual.Lunes, actual.Martes, actual.Miercoles, actual.Jueves, actual.Viernes, actual.Sabado, actual.Domingo, Id_Usuario = idUsuario, FechaMod = DateTime.Now, TipoMod = "Eliminado" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdPrecio = idPrecio }, transaction); return rowsAffected == 1; } public async Task DeleteByPublicacionIdAsync(int idPublicacion, int idUsuarioAuditoria, IDbTransaction transaction) // MODIFICADO: Recibe idUsuarioAuditoria { const string selectPrecios = "SELECT * FROM dbo.dist_Precios WHERE Id_Publicacion = @IdPublicacion"; var preciosAEliminar = await transaction.Connection!.QueryAsync(selectPrecios, new { IdPublicacion = idPublicacion }, transaction); const string sqlInsertHistorico = @" INSERT INTO dbo.dist_Precios_H (Id_Precio, Id_Publicacion, VigenciaD, VigenciaH, Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPrecio, @IdPublicacion, @VigenciaD, @VigenciaH, @Lunes, @Martes, @Miercoles, @Jueves, @Viernes, @Sabado, @Domingo, @Id_Usuario, @FechaMod, @TipoMod);"; foreach (var precio in preciosAEliminar) { await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { precio.IdPrecio, precio.IdPublicacion, precio.VigenciaD, precio.VigenciaH, precio.Lunes, precio.Martes, precio.Miercoles, precio.Jueves, precio.Viernes, precio.Sabado, precio.Domingo, Id_Usuario = idUsuarioAuditoria, // MODIFICADO: Usar el idUsuarioAuditoria pasado FechaMod = DateTime.Now, TipoMod = "Eliminado (Cascada)" }, transaction); } const string sql = "DELETE FROM dbo.dist_Precios WHERE Id_Publicacion = @IdPublicacion"; try { var rowsAffected = await transaction.Connection!.ExecuteAsync(sql, new { IdPublicacion = idPublicacion }, transaction: transaction); // No necesitamos devolver rowsAffected >= 0 si la lógica del servicio ya valida si debe haber registros return true; // Indica que la operación de borrado (incluyendo 0 filas) se intentó } catch (System.Exception ex) { _logger.LogError(ex, "Error al eliminar precios por IdPublicacion: {IdPublicacion}", idPublicacion); throw; } } } }