using Dapper; using GestionIntegral.Api.Models.Distribucion; using Microsoft.Extensions.Logging; // Para ILogger using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text; // Para StringBuilder using System.Threading.Tasks; namespace GestionIntegral.Api.Data.Repositories.Distribucion { public class CambioParadaRepository : ICambioParadaRepository { private readonly DbConnectionFactory _cf; private readonly ILogger _logger; public CambioParadaRepository(DbConnectionFactory cf, ILogger logger) { _cf = cf; _logger = logger; } private async Task LogHistorialAsync(CambioParadaCanilla paradaOriginal, int idUsuario, string tipoMod, IDbConnection connection, IDbTransaction? transaction) { var historial = new CambioParadaCanillaHistorial { Id_Registro = paradaOriginal.IdRegistro, Id_Canilla = paradaOriginal.IdCanilla, Parada = paradaOriginal.Parada, VigenciaD = paradaOriginal.VigenciaD, VigenciaH = paradaOriginal.VigenciaH, Id_Usuario = idUsuario, FechaMod = DateTime.Now, TipoMod = tipoMod }; const string sqlHistorial = @" INSERT INTO dbo.dist_CambiosParadasCanillas_H (Id_Registro, Id_Canilla, Parada, VigenciaD, VigenciaH, Id_Usuario, FechaMod, TipoMod) VALUES (@Id_Registro, @Id_Canilla, @Parada, @VigenciaD, @VigenciaH, @Id_Usuario, @FechaMod, @TipoMod);"; await connection.ExecuteAsync(sqlHistorial, historial, transaction); } public async Task> GetByCanillaAsync(int idCanilla) { using var connection = _cf.CreateConnection(); const string sql = @" SELECT Id_Registro AS IdRegistro, Id_Canilla AS IdCanilla, Parada, VigenciaD, VigenciaH FROM dbo.dist_CambiosParadasCanillas WHERE Id_Canilla = @IdCanilla ORDER BY VigenciaD DESC, Id_Registro DESC;"; return await connection.QueryAsync(sql, new { IdCanilla = idCanilla }); } public async Task GetByIdAsync(int idRegistro) { using var connection = _cf.CreateConnection(); const string sql = @" SELECT Id_Registro AS IdRegistro, Id_Canilla AS IdCanilla, Parada, VigenciaD, VigenciaH FROM dbo.dist_CambiosParadasCanillas WHERE Id_Registro = @IdRegistro;"; return await connection.QuerySingleOrDefaultAsync(sql, new { IdRegistro = idRegistro }); } public async Task GetCurrentParadaAsync(int idCanilla, IDbTransaction? transaction = null) { const string sql = @" SELECT TOP 1 Id_Registro AS IdRegistro, Id_Canilla AS IdCanilla, Parada, VigenciaD, VigenciaH FROM dbo.dist_CambiosParadasCanillas WHERE Id_Canilla = @IdCanilla AND VigenciaH IS NULL ORDER BY VigenciaD DESC, Id_Registro DESC;"; // Por si hay un error y quedaron varias abiertas var conn = transaction?.Connection ?? _cf.CreateConnection(); bool manageConnection = transaction == null; if (manageConnection && conn.State != ConnectionState.Open) { if (conn is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else conn.Open(); } try { return await conn.QuerySingleOrDefaultAsync(sql, new { IdCanilla = idCanilla }, transaction); } finally { if (manageConnection && conn.State == ConnectionState.Open) { if (conn is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else conn.Close(); } } } public async Task CreateAsync(CambioParadaCanilla nuevaParada, int idUsuario, IDbTransaction transaction) { const string sqlInsert = @" INSERT INTO dbo.dist_CambiosParadasCanillas (Id_Canilla, Parada, VigenciaD, VigenciaH) OUTPUT INSERTED.Id_Registro AS IdRegistro, INSERTED.Id_Canilla AS IdCanilla, INSERTED.Parada, INSERTED.VigenciaD, INSERTED.VigenciaH VALUES (@IdCanilla, @Parada, @VigenciaD, @VigenciaH);"; // VigenciaH es null al crear una nueva parada activa var inserted = await transaction.Connection!.QuerySingleAsync(sqlInsert, new { nuevaParada.IdCanilla, nuevaParada.Parada, nuevaParada.VigenciaD, VigenciaH = (DateTime?)null }, transaction); if (inserted == null || inserted.IdRegistro == 0) throw new DataException("Error al crear cambio de parada o ID no generado."); await LogHistorialAsync(inserted, idUsuario, "Creada", transaction.Connection!, transaction); return inserted; } public async Task UpdateVigenciaHAsync(int idRegistro, DateTime vigenciaH, int idUsuario, IDbTransaction transaction) { var paradaOriginal = await GetByIdAsync(idRegistro); if (paradaOriginal == null) throw new KeyNotFoundException("Registro de parada no encontrado para actualizar VigenciaH."); var paradaParaHistorial = new CambioParadaCanilla { IdRegistro = paradaOriginal.IdRegistro, IdCanilla = paradaOriginal.IdCanilla, Parada = paradaOriginal.Parada, VigenciaD = paradaOriginal.VigenciaD, VigenciaH = vigenciaH.Date }; // Loggear el estado que QUEDARÁ en la tabla principal (con la VigenciaH actualizada) // El TipoMod debería reflejar la acción. "Cerrada". await LogHistorialAsync(paradaParaHistorial, idUsuario, "Cerrada", transaction.Connection!, transaction); const string sqlUpdate = @" UPDATE dbo.dist_CambiosParadasCanillas SET VigenciaH = @VigenciaH WHERE Id_Registro = @IdRegistro;"; var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, new { VigenciaH = vigenciaH.Date, IdRegistro = idRegistro }, transaction); return rowsAffected == 1; } public async Task DeleteAsync(int idRegistro, int idUsuario, IDbTransaction transaction) { var paradaOriginal = await GetByIdAsync(idRegistro); if (paradaOriginal == null) throw new KeyNotFoundException("Registro de parada no encontrado para eliminar."); // Loggear ANTES de eliminar await LogHistorialAsync(paradaOriginal, idUsuario, "Eliminada", transaction.Connection!, transaction); const string sqlDelete = "DELETE FROM dbo.dist_CambiosParadasCanillas WHERE Id_Registro = @IdRegistro;"; var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdRegistro = idRegistro }, transaction); return rowsAffected == 1; } public async Task> GetCambiosParadaHistorialAsync( DateTime? fechaDesde, DateTime? fechaHasta, int? idUsuarioModifico, string? tipoModificacion, int? idCanillaOriginal) { using var connection = _cf.CreateConnection(); var sqlBuilder = new StringBuilder(@" SELECT h.Id_Registro, h.Id_Canilla, h.Parada, h.VigenciaD, h.VigenciaH, h.Id_Usuario, h.FechaMod, h.TipoMod, u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico, c.NomApe AS NombreCanilla FROM dbo.dist_CambiosParadasCanillas_H h JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id JOIN dbo.dist_dtCanillas c ON h.Id_Canilla = c.Id_Canilla 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 (idCanillaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Canilla = @IdCanillaOriginalParam"); parameters.Add("IdCanillaOriginalParam", idCanillaOriginal.Value); } sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); try { var result = await connection.QueryAsync( sqlBuilder.ToString(), (hist, userMod, canillaNombre) => (hist, userMod, canillaNombre), parameters, splitOn: "NombreUsuarioModifico,NombreCanilla" ); return result; } catch (Exception ex) { _logger.LogError(ex, "Error al obtener historial de Cambios de Parada."); return Enumerable.Empty<(CambioParadaCanillaHistorial, string, string)>(); } } } }