using GestionIntegral.Api.Data; // Para DbConnectionFactory using GestionIntegral.Api.Data.Repositories.Impresion; using GestionIntegral.Api.Dtos.Impresion; using GestionIntegral.Api.Models.Impresion; // Para Planta using Microsoft.Extensions.Logging; using System.Collections.Generic; using System.Data; // Para IsolationLevel using System.Linq; using System.Threading.Tasks; // Todavía usamos async para operaciones de BD namespace GestionIntegral.Api.Services.Impresion { public class PlantaService : IPlantaService { private readonly IPlantaRepository _plantaRepository; private readonly DbConnectionFactory _connectionFactory; // Para manejar transacciones private readonly ILogger _logger; public PlantaService(IPlantaRepository plantaRepository, DbConnectionFactory connectionFactory, ILogger logger) { _plantaRepository = plantaRepository; _connectionFactory = connectionFactory; _logger = logger; } // Método para mapear Planta a PlantaDto private PlantaDto MapToDto(Planta planta) => new PlantaDto { IdPlanta = planta.IdPlanta, Nombre = planta.Nombre, Detalle = planta.Detalle }; public async Task> ObtenerTodasAsync(string? nombreFilter, string? detalleFilter) { // Las operaciones de lectura no suelen necesitar transacción explícita aquí var plantas = await _plantaRepository.GetAllAsync(nombreFilter, detalleFilter); return plantas.Select(MapToDto); } public async Task ObtenerPorIdAsync(int id) { var planta = await _plantaRepository.GetByIdAsync(id); return planta == null ? null : MapToDto(planta); } public async Task<(PlantaDto? Planta, string? Error)> CrearAsync(CreatePlantaDto createDto, int idUsuario) { if (await _plantaRepository.ExistsByNameAsync(createDto.Nombre)) { return (null, "El nombre de la planta ya existe."); } var nuevaPlanta = new Planta { Nombre = createDto.Nombre, Detalle = createDto.Detalle }; using var connection = _connectionFactory.CreateConnection(); // Abrir la conexión asíncronamente si es posible if (connection is System.Data.Common.DbConnection dbConnection) { await dbConnection.OpenAsync(); } else { connection.Open(); // Fallback síncrono } // Empezar transacción using var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); try { var plantaCreada = await _plantaRepository.CreateAsync(nuevaPlanta, idUsuario, transaction); if (plantaCreada == null) { throw new DataException("La creación en el repositorio devolvió null."); } transaction.Commit(); // <--- CORREGIDO: Commit síncrono _logger.LogInformation("Planta ID {IdPlanta} creada exitosamente por Usuario ID {IdUsuario}.", plantaCreada.IdPlanta, idUsuario); return (MapToDto(plantaCreada), null); } catch (Exception ex) { try { transaction.Rollback(); // <--- CORREGIDO: Rollback síncrono } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en CrearAsync Planta."); } _logger.LogError(ex, "Error en transacción CrearAsync para Planta. Nombre: {Nombre}", createDto.Nombre); return (null, $"Error interno al crear la planta: {ex.Message}"); // Devolver mensaje de error } // La conexión y transacción se disponen automáticamente al salir del 'using' } public async Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdatePlantaDto updateDto, int idUsuario) { if (await _plantaRepository.ExistsByNameAsync(updateDto.Nombre, id)) { return (false, "El nombre de la planta ya existe para otro registro."); } var plantaAActualizar = new Planta { IdPlanta = id, Nombre = updateDto.Nombre, Detalle = updateDto.Detalle }; using var connection = _connectionFactory.CreateConnection(); if (connection is System.Data.Common.DbConnection dbConnection) { await dbConnection.OpenAsync(); } else { connection.Open(); } using var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); try { var actualizado = await _plantaRepository.UpdateAsync(plantaAActualizar, idUsuario, transaction); if (!actualizado) { // La excepción KeyNotFoundException se manejará en el bloque catch // Aquí asumimos que si devuelve false es porque no afectó filas, lo cual podría ser un error lógico throw new DataException("La operación de actualización no afectó ninguna fila."); } transaction.Commit(); // <--- CORREGIDO: Commit síncrono _logger.LogInformation("Planta ID {IdPlanta} actualizada exitosamente por Usuario ID {IdUsuario}.", id, idUsuario); return (true, null); } catch (KeyNotFoundException knfex) // Captura específica si el repo la lanza { try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en ActualizarAsync Planta."); } _logger.LogWarning(knfex, "Intento de actualizar Planta ID: {Id} no encontrada.", id); return (false, "Planta no encontrada."); } catch (Exception ex) { try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en ActualizarAsync Planta."); } _logger.LogError(ex, "Error en transacción ActualizarAsync para Planta ID: {Id}", id); return (false, $"Error interno al actualizar la planta: {ex.Message}"); } } public async Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario) { if (await _plantaRepository.IsInUseAsync(id)) { return (false, "No se puede eliminar. La planta está siendo utilizada."); } using var connection = _connectionFactory.CreateConnection(); if (connection is System.Data.Common.DbConnection dbConnection) { await dbConnection.OpenAsync(); } else { connection.Open(); } using var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); try { var eliminado = await _plantaRepository.DeleteAsync(id, idUsuario, transaction); if (!eliminado) { throw new DataException("La operación de eliminación no afectó ninguna fila."); } transaction.Commit(); // <--- CORREGIDO: Commit síncrono _logger.LogInformation("Planta ID {IdPlanta} eliminada exitosamente por Usuario ID {IdUsuario}.", id, idUsuario); return (true, null); } catch (KeyNotFoundException knfex) // Captura específica si el repo la lanza { try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en EliminarAsync Planta."); } _logger.LogWarning(knfex, "Intento de eliminar Planta ID: {Id} no encontrada.", id); return (false, "Planta no encontrada."); } catch (Exception ex) { try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en EliminarAsync Planta."); } _logger.LogError(ex, "Error en transacción EliminarAsync para Planta ID: {Id}", id); return (false, $"Error interno al eliminar la planta: {ex.Message}"); } } } }