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 SalidaOtroDestinoService : ISalidaOtroDestinoService { private readonly ISalidaOtroDestinoRepository _salidaRepository; private readonly IPublicacionRepository _publicacionRepository; private readonly IOtroDestinoRepository _otroDestinoRepository; private readonly DbConnectionFactory _connectionFactory; private readonly ILogger _logger; public SalidaOtroDestinoService( ISalidaOtroDestinoRepository salidaRepository, IPublicacionRepository publicacionRepository, IOtroDestinoRepository otroDestinoRepository, DbConnectionFactory connectionFactory, ILogger logger) { _salidaRepository = salidaRepository; _publicacionRepository = publicacionRepository; _otroDestinoRepository = otroDestinoRepository; _connectionFactory = connectionFactory; _logger = logger; } private async Task MapToDto(SalidaOtroDestino salida) { if (salida == null) return null!; // Debería ser manejado por el llamador var publicacion = await _publicacionRepository.GetByIdSimpleAsync(salida.IdPublicacion); var destino = await _otroDestinoRepository.GetByIdAsync(salida.IdDestino); return new SalidaOtroDestinoDto { IdParte = salida.IdParte, IdPublicacion = salida.IdPublicacion, NombrePublicacion = publicacion?.Nombre ?? "Publicación Desconocida", IdDestino = salida.IdDestino, NombreDestino = destino?.Nombre ?? "Destino Desconocido", Fecha = salida.Fecha.ToString("yyyy-MM-dd"), Cantidad = salida.Cantidad, Observacion = salida.Observacion }; } public async Task> ObtenerTodosAsync(DateTime? fechaDesde, DateTime? fechaHasta, int? idPublicacion, int? idDestino) { var salidas = await _salidaRepository.GetAllAsync(fechaDesde, fechaHasta, idPublicacion, idDestino); var dtos = new List(); foreach (var salida in salidas) { dtos.Add(await MapToDto(salida)); } return dtos; } public async Task ObtenerPorIdAsync(int idParte) { var salida = await _salidaRepository.GetByIdAsync(idParte); return salida == null ? null : await MapToDto(salida); } public async Task<(SalidaOtroDestinoDto? Salida, string? Error)> CrearAsync(CreateSalidaOtroDestinoDto createDto, int idUsuario) { if (await _publicacionRepository.GetByIdSimpleAsync(createDto.IdPublicacion) == null) return (null, "La publicación especificada no existe o no está habilitada."); if (await _otroDestinoRepository.GetByIdAsync(createDto.IdDestino) == null) return (null, "El destino especificado no existe."); var nuevaSalida = new SalidaOtroDestino { IdPublicacion = createDto.IdPublicacion, IdDestino = createDto.IdDestino, Fecha = createDto.Fecha.Date, Cantidad = createDto.Cantidad, Observacion = createDto.Observacion }; using var connection = _connectionFactory.CreateConnection(); if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); using var transaction = connection.BeginTransaction(); try { var salidaCreada = await _salidaRepository.CreateAsync(nuevaSalida, idUsuario, transaction); if (salidaCreada == null) throw new DataException("Error al registrar la salida."); transaction.Commit(); _logger.LogInformation("SalidaOtroDestino ID {Id} creada por Usuario ID {UserId}.", salidaCreada.IdParte, idUsuario); return (await MapToDto(salidaCreada), null); } catch (Exception ex) { try { transaction.Rollback(); } catch { } _logger.LogError(ex, "Error CrearAsync SalidaOtroDestino para Pub ID {IdPub}", createDto.IdPublicacion); return (null, $"Error interno: {ex.Message}"); } } public async Task<(bool Exito, string? Error)> ActualizarAsync(int idParte, UpdateSalidaOtroDestinoDto updateDto, int idUsuario) { using var connection = _connectionFactory.CreateConnection(); if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); using var transaction = connection.BeginTransaction(); try { var salidaExistente = await _salidaRepository.GetByIdAsync(idParte); // Obtener dentro de TX if (salidaExistente == null) return (false, "Registro de salida no encontrado."); // Actualizar solo los campos permitidos salidaExistente.Cantidad = updateDto.Cantidad; salidaExistente.Observacion = updateDto.Observacion; var actualizado = await _salidaRepository.UpdateAsync(salidaExistente, idUsuario, transaction); if (!actualizado) throw new DataException("Error al actualizar la salida."); transaction.Commit(); _logger.LogInformation("SalidaOtroDestino ID {Id} actualizada por Usuario ID {UserId}.", idParte, idUsuario); return (true, null); } catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Registro no encontrado."); } catch (Exception ex) { try { transaction.Rollback(); } catch { } _logger.LogError(ex, "Error ActualizarAsync SalidaOtroDestino ID: {Id}", idParte); return (false, $"Error interno: {ex.Message}"); } } public async Task<(bool Exito, string? Error)> EliminarAsync(int idParte, int idUsuario) { using var connection = _connectionFactory.CreateConnection(); if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); using var transaction = connection.BeginTransaction(); try { var salidaExistente = await _salidaRepository.GetByIdAsync(idParte); if (salidaExistente == null) return (false, "Registro de salida no encontrado."); var eliminado = await _salidaRepository.DeleteAsync(idParte, idUsuario, transaction); if (!eliminado) throw new DataException("Error al eliminar la salida."); transaction.Commit(); _logger.LogInformation("SalidaOtroDestino ID {Id} eliminada por Usuario ID {UserId}.", idParte, idUsuario); return (true, null); } catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Registro no encontrado."); } catch (Exception ex) { try { transaction.Rollback(); } catch { } _logger.LogError(ex, "Error EliminarAsync SalidaOtroDestino ID: {Id}", idParte); return (false, $"Error interno: {ex.Message}"); } } } }