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.Collections.Generic; using System.Data; using System.Linq; using System.Threading.Tasks; namespace GestionIntegral.Api.Services.Distribucion { public class CanillaService : ICanillaService { private readonly ICanillaRepository _canillaRepository; private readonly IZonaRepository _zonaRepository; private readonly IEmpresaRepository _empresaRepository; private readonly DbConnectionFactory _connectionFactory; private readonly ILogger _logger; public CanillaService( ICanillaRepository canillaRepository, IZonaRepository zonaRepository, IEmpresaRepository empresaRepository, DbConnectionFactory connectionFactory, ILogger logger) { _canillaRepository = canillaRepository; _zonaRepository = zonaRepository; _empresaRepository = empresaRepository; _connectionFactory = connectionFactory; _logger = logger; } // CORREGIDO: MapToDto ahora acepta una tupla con tipos anulables private CanillaDto? MapToDto((Canilla? Canilla, string? NombreZona, string? NombreEmpresa) data) { if (data.Canilla == null) return null; return new CanillaDto { IdCanilla = data.Canilla.IdCanilla, Legajo = data.Canilla.Legajo, NomApe = data.Canilla.NomApe, Parada = data.Canilla.Parada, IdZona = data.Canilla.IdZona, NombreZona = data.NombreZona ?? "N/A", // Manejar null Accionista = data.Canilla.Accionista, Obs = data.Canilla.Obs, Empresa = data.Canilla.Empresa, NombreEmpresa = data.NombreEmpresa ?? "N/A (Accionista)", // Manejar null Baja = data.Canilla.Baja, FechaBaja = data.Canilla.FechaBaja?.ToString("dd/MM/yyyy") }; } public async Task> ObtenerTodosAsync(string? nomApeFilter, int? legajoFilter, bool? esAccionista, bool? soloActivos) { var data = await _canillaRepository.GetAllAsync(nomApeFilter, legajoFilter, soloActivos, esAccionista); // Filtrar nulos y asegurar al compilador que no hay nulos en la lista final return data.Select(MapToDto).Where(dto => dto != null).Select(dto => dto!); } public async Task ObtenerPorIdAsync(int id) { var data = await _canillaRepository.GetByIdAsync(id); // MapToDto ahora devuelve CanillaDto? así que esto es correcto return MapToDto(data); } public async Task<(CanillaDto? Canilla, string? Error)> CrearAsync(CreateCanillaDto createDto, int idUsuario) { if (createDto.Legajo.HasValue && createDto.Legajo != 0 && await _canillaRepository.ExistsByLegajoAsync(createDto.Legajo.Value)) { return (null, "El legajo ingresado ya existe para otro canillita."); } var zona = await _zonaRepository.GetByIdAsync(createDto.IdZona); // GetByIdAsync de Zona ya considera solo activas if (zona == null) { return (null, "La zona seleccionada no es válida o no está activa."); } if (createDto.Empresa != 0) // Solo validar empresa si no es 0 { var empresa = await _empresaRepository.GetByIdAsync(createDto.Empresa); if (empresa == null) { return (null, "La empresa seleccionada no es válida."); } } // CORREGIDO: Usar directamente el valor booleano if (createDto.Accionista == true && createDto.Empresa != 0) { return (null, "Un canillita accionista no debe tener una empresa asignada (Empresa debe ser 0)."); } if (createDto.Accionista == false && createDto.Empresa == 0) { return (null, "Un canillita no accionista debe tener una empresa asignada (Empresa no puede ser 0)."); } var nuevoCanilla = new Canilla { Legajo = createDto.Legajo == 0 ? null : createDto.Legajo, NomApe = createDto.NomApe, Parada = createDto.Parada, IdZona = createDto.IdZona, Accionista = createDto.Accionista, Obs = createDto.Obs, Empresa = createDto.Empresa, Baja = false, FechaBaja = null }; 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 canillaCreado = await _canillaRepository.CreateAsync(nuevoCanilla, idUsuario, transaction); if (canillaCreado == null) throw new DataException("Error al crear el canillita."); transaction.Commit(); // Para el DTO de respuesta, necesitamos NombreZona y NombreEmpresa string nombreEmpresaParaDto = "N/A (Accionista)"; if (canillaCreado.Empresa != 0) { var empresaData = await _empresaRepository.GetByIdAsync(canillaCreado.Empresa); nombreEmpresaParaDto = empresaData?.Nombre ?? "Empresa Desconocida"; } var dtoCreado = new CanillaDto { IdCanilla = canillaCreado.IdCanilla, Legajo = canillaCreado.Legajo, NomApe = canillaCreado.NomApe, Parada = canillaCreado.Parada, IdZona = canillaCreado.IdZona, NombreZona = zona.Nombre, // Usar nombre de zona ya obtenido Accionista = canillaCreado.Accionista, Obs = canillaCreado.Obs, Empresa = canillaCreado.Empresa, NombreEmpresa = nombreEmpresaParaDto, Baja = canillaCreado.Baja, FechaBaja = null }; _logger.LogInformation("Canilla ID {IdCanilla} creado por Usuario ID {IdUsuario}.", canillaCreado.IdCanilla, idUsuario); return (dtoCreado, null); } catch (Exception ex) { try { transaction.Rollback(); } catch { } _logger.LogError(ex, "Error CrearAsync Canilla: {NomApe}", createDto.NomApe); return (null, $"Error interno al crear el canillita: {ex.Message}"); } } public async Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdateCanillaDto updateDto, int idUsuario) { var canillaExistente = await _canillaRepository.GetByIdSimpleAsync(id); if (canillaExistente == null) return (false, "Canillita no encontrado."); if (updateDto.Legajo.HasValue && updateDto.Legajo != 0 && await _canillaRepository.ExistsByLegajoAsync(updateDto.Legajo.Value, id)) { return (false, "El legajo ingresado ya existe para otro canillita."); } if (await _zonaRepository.GetByIdAsync(updateDto.IdZona) == null) // GetByIdAsync de Zona ya considera solo activas { return (false, "La zona seleccionada no es válida o no está activa."); } if (updateDto.Empresa != 0) // Solo validar empresa si no es 0 { var empresa = await _empresaRepository.GetByIdAsync(updateDto.Empresa); if (empresa == null) { return (false, "La empresa seleccionada no es válida."); } } // Usar directamente el valor booleano para Accionista if (updateDto.Accionista == true && updateDto.Empresa != 0) { // Al ser 'bool', no puede ser null. La comparación explícita con 'true'/'false' es para claridad. return (false, "Un canillita accionista no debe tener una empresa asignada (Empresa debe ser 0)."); } if (updateDto.Accionista == false && updateDto.Empresa == 0) { return (false, "Un canillita no accionista debe tener una empresa asignada (Empresa no puede ser 0)."); } // Mapear DTO a entidad existente canillaExistente.Legajo = updateDto.Legajo == 0 ? null : updateDto.Legajo; canillaExistente.NomApe = updateDto.NomApe; canillaExistente.Parada = updateDto.Parada; canillaExistente.IdZona = updateDto.IdZona; canillaExistente.Accionista = updateDto.Accionista; // Aquí Accionista ya es bool canillaExistente.Obs = updateDto.Obs; canillaExistente.Empresa = updateDto.Empresa; // Baja y FechaBaja se manejan por ToggleBajaAsync 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 actualizado = await _canillaRepository.UpdateAsync(canillaExistente, idUsuario, transaction); if (!actualizado) throw new DataException("Error al actualizar el canillita."); transaction.Commit(); _logger.LogInformation("Canilla ID {IdCanilla} actualizado por Usuario ID {IdUsuario}.", id, idUsuario); return (true, null); } catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Canillita no encontrado durante la actualización."); } catch (Exception ex) { try { transaction.Rollback(); } catch { } _logger.LogError(ex, "Error ActualizarAsync Canilla ID: {IdCanilla}", id); return (false, $"Error interno al actualizar el canillita: {ex.Message}"); } } public async Task<(bool Exito, string? Error)> ToggleBajaAsync(int id, bool darDeBaja, int idUsuario) { var canilla = await _canillaRepository.GetByIdSimpleAsync(id); if (canilla == null) return (false, "Canillita no encontrado."); if (canilla.Baja == darDeBaja) { return (false, darDeBaja ? "El canillita ya está dado de baja." : "El canillita ya está activo."); } DateTime? fechaBaja = darDeBaja ? DateTime.Now : (DateTime?)null; 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 success = await _canillaRepository.ToggleBajaAsync(id, darDeBaja, fechaBaja, idUsuario, transaction); if (!success) throw new DataException("Error al cambiar estado de baja del canillita."); transaction.Commit(); _logger.LogInformation("Estado de baja cambiado a {EstadoBaja} para Canilla ID {IdCanilla} por Usuario ID {IdUsuario}.", darDeBaja, id, idUsuario); return (true, null); } catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Canillita no encontrado durante el cambio de estado de baja."); } catch (Exception ex) { try { transaction.Rollback(); } catch { } _logger.LogError(ex, "Error ToggleBajaAsync Canilla ID: {IdCanilla}", id); return (false, $"Error interno al cambiar estado de baja: {ex.Message}"); } } } }