using Dapper; using GestionIntegral.Api.Models.Distribucion; using System.Collections.Generic; using System.Data; using System.Text; // Para StringBuilder using System.Threading.Tasks; namespace GestionIntegral.Api.Data.Repositories.Distribucion { public class ZonaRepository : IZonaRepository { private readonly DbConnectionFactory _connectionFactory; private readonly ILogger _logger; public ZonaRepository(DbConnectionFactory connectionFactory, ILogger logger) { _connectionFactory = connectionFactory; _logger = logger; } public async Task> GetAllAsync(string? nombreFilter, string? descripcionFilter, bool soloActivas = true) { var sqlBuilder = new StringBuilder("SELECT Id_Zona AS IdZona, Nombre, Descripcion, Estado FROM dbo.dist_dtZonas WHERE 1=1"); var parameters = new DynamicParameters(); if (soloActivas) { sqlBuilder.Append(" AND Estado = 1"); } if (!string.IsNullOrWhiteSpace(nombreFilter)) { sqlBuilder.Append(" AND Nombre LIKE @NombreFilter"); parameters.Add("NombreFilter", $"%{nombreFilter}%"); } if (!string.IsNullOrWhiteSpace(descripcionFilter)) { sqlBuilder.Append(" AND Descripcion LIKE @DescripcionFilter"); parameters.Add("DescripcionFilter", $"%{descripcionFilter}%"); } sqlBuilder.Append(" ORDER BY Nombre;"); try { using (var connection = _connectionFactory.CreateConnection()) { return await connection.QueryAsync(sqlBuilder.ToString(), parameters); } } catch (Exception ex) { _logger.LogError(ex, "Error al obtener todas las Zonas. Filtros: Nombre={NombreFilter}, Descripcion={DescFilter}", nombreFilter, descripcionFilter); return Enumerable.Empty(); } } public async Task GetByIdAsync(int id, bool soloActivas = true) { var sqlBuilder = new StringBuilder("SELECT Id_Zona AS IdZona, Nombre, Descripcion, Estado FROM dbo.dist_dtZonas WHERE Id_Zona = @Id"); if (soloActivas) { sqlBuilder.Append(" AND Estado = 1"); } try { using (var connection = _connectionFactory.CreateConnection()) { return await connection.QuerySingleOrDefaultAsync(sqlBuilder.ToString(), new { Id = id }); } } catch (Exception ex) { _logger.LogError(ex, "Error al obtener Zona por ID: {IdZona}", id); return null; } } public async Task ExistsByNameAsync(string nombre, int? excludeId = null, bool soloActivas = true) { var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.dist_dtZonas WHERE Nombre = @Nombre"); var parameters = new DynamicParameters(); parameters.Add("Nombre", nombre); if (soloActivas) { sqlBuilder.Append(" AND Estado = 1"); } if (excludeId.HasValue) { sqlBuilder.Append(" AND Id_Zona != @ExcludeId"); parameters.Add("ExcludeId", excludeId.Value); } try { using (var connection = _connectionFactory.CreateConnection()) { var count = await connection.ExecuteScalarAsync(sqlBuilder.ToString(), parameters); return count > 0; } } catch (Exception ex) { _logger.LogError(ex, "Error en ExistsByNameAsync para Zona con nombre: {Nombre}", nombre); return true; } } public async Task CreateAsync(Zona nuevaZona, int idUsuario) { // Las nuevas zonas siempre se crean con Estado = 1 var sqlInsert = @" INSERT INTO dbo.dist_dtZonas (Nombre, Descripcion, Estado) OUTPUT INSERTED.Id_Zona AS IdZona, INSERTED.Nombre, INSERTED.Descripcion, INSERTED.Estado VALUES (@Nombre, @Descripcion, 1);"; var sqlInsertHistorico = @" INSERT INTO dbo.dist_dtZonas_H (Id_Zona, Nombre, Descripcion, Estado, Id_Usuario, FechaMod, TipoMod) VALUES (@IdZona, @Nombre, @Descripcion, @Estado, @IdUsuario, @FechaMod, @TipoMod);"; try { using (var connection = _connectionFactory.CreateConnection()) { connection.Open(); using (var transaction = connection.BeginTransaction()) { try { var insertedZona = await connection.QuerySingleAsync( sqlInsert, new { nuevaZona.Nombre, nuevaZona.Descripcion }, transaction: transaction ); await connection.ExecuteAsync(sqlInsertHistorico, new { IdZona = insertedZona.IdZona, insertedZona.Nombre, insertedZona.Descripcion, Estado = insertedZona.Estado, // Será true (1) IdUsuario = idUsuario, FechaMod = DateTime.Now, TipoMod = "Insertada" }, transaction: transaction); transaction.Commit(); return insertedZona; } catch (Exception exTrans) { transaction.Rollback(); _logger.LogError(exTrans, "Error en transacción CreateAsync para Zona. Nombre: {Nombre}", nuevaZona.Nombre); return null; } } } } catch (Exception ex) { _logger.LogError(ex, "Error general en CreateAsync para Zona. Nombre: {Nombre}", nuevaZona.Nombre); return null; } } public async Task UpdateAsync(Zona zonaAActualizar, int idUsuario) { var zonaActual = await GetByIdAsync(zonaAActualizar.IdZona, soloActivas: false); // Obtener incluso si está inactiva para el historial if (zonaActual == null) return false; // Solo actualizamos si está activa, pero el historial registra el estado que tenía. // El servicio decidirá si se puede actualizar una zona inactiva. var sqlUpdate = @" UPDATE dbo.dist_dtZonas SET Nombre = @Nombre, Descripcion = @Descripcion WHERE Id_Zona = @IdZona;"; // Podríamos añadir AND Estado = 1 si el servicio lo requiere así estrictamente. var sqlInsertHistorico = @" INSERT INTO dbo.dist_dtZonas_H (Id_Zona, Nombre, Descripcion, Estado, Id_Usuario, FechaMod, TipoMod) VALUES (@IdZona, @NombreActual, @DescripcionActual, @EstadoActual, @IdUsuario, @FechaMod, @TipoMod);"; try { using (var connection = _connectionFactory.CreateConnection()) { connection.Open(); using (var transaction = connection.BeginTransaction()) { try { // Registrar el estado *antes* de la modificación await connection.ExecuteAsync(sqlInsertHistorico, new { IdZona = zonaActual.IdZona, NombreActual = zonaActual.Nombre, DescripcionActual = zonaActual.Descripcion, EstadoActual = zonaActual.Estado, IdUsuario = idUsuario, FechaMod = DateTime.Now, TipoMod = "Modificada" }, transaction: transaction); var rowsAffected = await connection.ExecuteAsync(sqlUpdate, new { zonaAActualizar.Nombre, zonaAActualizar.Descripcion, zonaAActualizar.IdZona }, transaction: transaction); transaction.Commit(); return rowsAffected == 1; } catch (Exception exTrans) { transaction.Rollback(); _logger.LogError(exTrans, "Error en transacción UpdateAsync para Zona ID: {IdZona}", zonaAActualizar.IdZona); return false; } } } } catch (Exception ex) { _logger.LogError(ex, "Error general en UpdateAsync para Zona ID: {IdZona}", zonaAActualizar.IdZona); return false; } } public async Task SoftDeleteAsync(int id, int idUsuario) { var zonaActual = await GetByIdAsync(id, soloActivas: false); // Obtenerla sin importar su estado para el historial if (zonaActual == null) return false; if (!zonaActual.Estado) return true; // Ya está "eliminada" (inactiva), considerar éxito var sqlSoftDelete = @"UPDATE dbo.dist_dtZonas SET Estado = 0 WHERE Id_Zona = @IdZona;"; var sqlInsertHistorico = @" INSERT INTO dbo.dist_dtZonas_H (Id_Zona, Nombre, Descripcion, Estado, Id_Usuario, FechaMod, TipoMod) VALUES (@IdZona, @Nombre, @Descripcion, @EstadoNuevo, @IdUsuario, @FechaMod, @TipoMod);"; try { using (var connection = _connectionFactory.CreateConnection()) { connection.Open(); using (var transaction = connection.BeginTransaction()) { try { // Actualizar la tabla principal var rowsAffected = await connection.ExecuteAsync(sqlSoftDelete, new { IdZona = id }, transaction: transaction); if (rowsAffected == 1) { // Registrar en el historial con el nuevo estado (0) await connection.ExecuteAsync(sqlInsertHistorico, new { IdZona = zonaActual.IdZona, zonaActual.Nombre, zonaActual.Descripcion, EstadoNuevo = false, // El estado después del soft delete IdUsuario = idUsuario, FechaMod = DateTime.Now, TipoMod = "Eliminada" // O "Deshabilitada" }, transaction: transaction); } transaction.Commit(); return rowsAffected == 1; } catch (Exception exTrans) { transaction.Rollback(); _logger.LogError(exTrans, "Error en transacción SoftDeleteAsync para Zona ID: {IdZona}", id); return false; } } } } catch (Exception ex) { _logger.LogError(ex, "Error general en SoftDeleteAsync para Zona ID: {IdZona}", id); return false; } } public async Task IsInUseAsync(int id) { // Verifica en dist_dtDistribuidores y dist_dtCanillas var sqlCheckDist = "SELECT COUNT(1) FROM dbo.dist_dtDistribuidores WHERE Id_Zona = @IdZona"; var sqlCheckCanillas = "SELECT COUNT(1) FROM dbo.dist_dtCanillas WHERE Id_Zona = @IdZona AND Baja = 0"; // Solo canillas activos try { using (var connection = _connectionFactory.CreateConnection()) { var countDist = await connection.ExecuteScalarAsync(sqlCheckDist, new { IdZona = id }); if (countDist > 0) return true; var countCanillas = await connection.ExecuteScalarAsync(sqlCheckCanillas, new { IdZona = id }); return countCanillas > 0; } } catch (Exception ex) { _logger.LogError(ex, "Error en IsInUseAsync para Zona ID: {IdZona}", id); return true; // Asumir que está en uso si hay error } } } }