using GestionIntegral.Api.Data; using GestionIntegral.Api.Data.Repositories.Distribucion; using GestionIntegral.Api.Dtos.Distribucion; using GestionIntegral.Api.Models.Distribucion; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Threading.Tasks; namespace GestionIntegral.Api.Services.Distribucion { public class CambioParadaService : ICambioParadaService { private readonly ICambioParadaRepository _paradaRepo; private readonly ICanillaRepository _canillaRepo; // Para nombre y validación private readonly DbConnectionFactory _connectionFactory; private readonly ILogger _logger; public CambioParadaService( ICambioParadaRepository paradaRepo, ICanillaRepository canillaRepo, DbConnectionFactory connectionFactory, ILogger logger) { _paradaRepo = paradaRepo; _canillaRepo = canillaRepo; _connectionFactory = connectionFactory; _logger = logger; } private async Task MapToDto(CambioParadaCanilla parada) { var canillaData = await _canillaRepo.GetByIdAsync(parada.IdCanilla); return new CambioParadaDto { IdRegistro = parada.IdRegistro, IdCanilla = parada.IdCanilla, NombreCanilla = canillaData.Canilla?.NomApe ?? "N/A", Parada = parada.Parada, VigenciaD = parada.VigenciaD.ToString("yyyy-MM-dd"), VigenciaH = parada.VigenciaH?.ToString("yyyy-MM-dd") }; } public async Task> ObtenerPorCanillaAsync(int idCanilla) { var paradas = await _paradaRepo.GetByCanillaAsync(idCanilla); var dtos = new List(); foreach (var p in paradas) { dtos.Add(await MapToDto(p)); } return dtos; } public async Task ObtenerPorIdAsync(int idRegistro) { var parada = await _paradaRepo.GetByIdAsync(idRegistro); return parada == null ? null : await MapToDto(parada); } public async Task<(CambioParadaDto? Parada, string? Error)> CrearNuevaParadaAsync(int idCanilla, CreateCambioParadaDto createDto, int idUsuario) { var canilla = await _canillaRepo.GetByIdSimpleAsync(idCanilla); if (canilla == null) return (null, "Canillita no encontrado."); if (createDto.VigenciaD.Date < DateTime.Today.AddYears(-5) || createDto.VigenciaD.Date > DateTime.Today.AddYears(5)) // Validación básica de fecha return (null, "Fecha de Vigencia Desde inválida."); using var connection = _connectionFactory.CreateConnection(); if (connection.State != ConnectionState.Open) { if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); } using var transaction = connection.BeginTransaction(); try { // 1. Buscar la parada activa actual (si existe) para cerrarla var paradaActual = await _paradaRepo.GetCurrentParadaAsync(idCanilla, transaction); if (paradaActual != null) { if (createDto.VigenciaD.Date <= paradaActual.VigenciaD.Date) { transaction.Rollback(); return (null, "La Vigencia Desde de la nueva parada debe ser posterior a la Vigencia Desde de la parada activa actual."); } // Cerrar la parada actual estableciendo VigenciaH al día anterior de la nueva VigenciaD DateTime vigenciaHAactual = createDto.VigenciaD.Date.AddDays(-1); bool cerrada = await _paradaRepo.UpdateVigenciaHAsync(paradaActual.IdRegistro, vigenciaHAactual, idUsuario, transaction); if (!cerrada) throw new DataException("No se pudo cerrar la parada activa anterior."); _logger.LogInformation("Parada anterior ID {IdRegistro} cerrada con VigenciaH {VigenciaH}", paradaActual.IdRegistro, vigenciaHAactual); } // 2. Crear la nueva parada var nuevaParada = new CambioParadaCanilla { IdCanilla = idCanilla, Parada = createDto.Parada, VigenciaD = createDto.VigenciaD.Date, VigenciaH = null // Nueva parada siempre inicia activa }; var paradaCreada = await _paradaRepo.CreateAsync(nuevaParada, idUsuario, transaction); if (paradaCreada == null) throw new DataException("Error al crear el nuevo registro de parada."); // 3. Actualizar la parada principal en dist_dtCanillas canilla.Parada = paradaCreada.Parada; // Actualizar el campo Parada en la tabla principal bool canillaActualizado = await _canillaRepo.UpdateAsync(canilla, idUsuario, transaction); // Asume que tu repo de canilla tiene UpdateAsync que toma la entidad if (!canillaActualizado) throw new DataException("Error al actualizar la parada principal del canillita."); transaction.Commit(); _logger.LogInformation("Nueva parada ID {IdRegistro} creada para Canilla ID {IdCanilla}. Parada principal actualizada.", paradaCreada.IdRegistro, idCanilla); return (await MapToDto(paradaCreada), null); } catch (Exception ex) { try { transaction.Rollback(); } catch { } _logger.LogError(ex, "Error en CrearNuevaParadaAsync para Canilla ID {IdCanilla}", idCanilla); return (null, $"Error interno: {ex.Message}"); } finally { if (connection.State == ConnectionState.Open) { if (connection is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else connection.Close(); } } } public async Task<(bool Exito, string? Error)> CerrarParadaAsync(int idRegistro, UpdateCambioParadaDto updateDto, int idUsuario) { // Este método es para cerrar una parada manualmente si es necesario, // por ejemplo, si se cometió un error y no se creó una nueva que la cierre. var paradaExistente = await _paradaRepo.GetByIdAsync(idRegistro); if (paradaExistente == null) return (false, "Registro de parada no encontrado."); if (paradaExistente.VigenciaH.HasValue) return (false, "Esta parada ya tiene una fecha de Vigencia Hasta."); if (updateDto.VigenciaH.Date < paradaExistente.VigenciaD.Date) return (false, "La Vigencia Hasta no puede ser anterior a la Vigencia Desde."); using var connection = _connectionFactory.CreateConnection(); if (connection.State != ConnectionState.Open) { if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); } using var transaction = connection.BeginTransaction(); try { bool actualizada = await _paradaRepo.UpdateVigenciaHAsync(idRegistro, updateDto.VigenciaH.Date, idUsuario, transaction); if (!actualizada) throw new DataException("Error al actualizar la Vigencia Hasta de la parada."); // Opcional: Si esta era la parada activa en dist_dtCanillas, podrías querer limpiarla o marcarla de alguna forma // Esto depende de si el campo 'Parada' en dist_dtCanillas siempre debe reflejar la última activa. // Por simplicidad, no se actualiza dist_dtCanillas aquí al cerrar una parada manualmente. transaction.Commit(); _logger.LogInformation("VigenciaH actualizada para Parada ID {IdRegistro}", idRegistro); return (true, null); } catch (Exception ex) { try { transaction.Rollback(); } catch { } _logger.LogError(ex, "Error al cerrar Parada ID {IdRegistro}", idRegistro); return (false, $"Error interno: {ex.Message}"); } finally { if (connection.State == ConnectionState.Open) { if (connection is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else connection.Close(); } } } public async Task<(bool Exito, string? Error)> EliminarParadaAsync(int idRegistro, int idUsuario) { // La eliminación puede ser problemática si rompe la secuencia histórica. // Considera si realmente quieres permitir esto o solo "cerrar" paradas. var paradaExistente = await _paradaRepo.GetByIdAsync(idRegistro); if (paradaExistente == null) return (false, "Registro de parada no encontrado."); if (paradaExistente.VigenciaH == null) return (false, "No se puede eliminar una parada activa. Primero debe cerrarla o crear una nueva que la reemplace."); using var connection = _connectionFactory.CreateConnection(); if (connection.State != ConnectionState.Open) { if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); } using var transaction = connection.BeginTransaction(); try { bool eliminado = await _paradaRepo.DeleteAsync(idRegistro, idUsuario, transaction); if (!eliminado) throw new DataException("Error al eliminar el registro de parada."); transaction.Commit(); _logger.LogInformation("Registro de Parada ID {IdRegistro} eliminado por Usuario ID {IdUsuario}", idRegistro, idUsuario); return (true, null); } catch (Exception ex) { try { transaction.Rollback(); } catch { } _logger.LogError(ex, "Error al eliminar Parada ID {IdRegistro}", idRegistro); return (false, $"Error interno: {ex.Message}"); } finally { if (connection.State == ConnectionState.Open) { if (connection is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else connection.Close(); } } } public async Task> ObtenerCambiosParadaHistorialAsync( DateTime? fechaDesde, DateTime? fechaHasta, int? idUsuarioModifico, string? tipoModificacion, int? idCanillaAfectado) { var historialData = await _paradaRepo.GetCambiosParadaHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idCanillaAfectado); return historialData.Select(h => new CambioParadaHistorialDto { Id_Registro = h.Historial.Id_Registro, Id_Canilla = h.Historial.Id_Canilla, NombreCanilla = h.NombreCanilla, Parada = h.Historial.Parada, VigenciaD = h.Historial.VigenciaD, VigenciaH = h.Historial.VigenciaH ?? default(DateTime), Id_Usuario = h.Historial.Id_Usuario, NombreUsuarioModifico = h.NombreUsuarioModifico, FechaMod = h.Historial.FechaMod, TipoMod = h.Historial.TipoMod }).ToList(); } } }