// src/Elecciones.Api/Controllers/ResultadosController.cs using Elecciones.Core.DTOs.ApiResponses; using Elecciones.Database; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Linq; using System.Threading.Tasks; namespace Elecciones.Api.Controllers; [ApiController] [Route("api/[controller]")] public class ResultadosController : ControllerBase { private readonly EleccionesDbContext _dbContext; private readonly ILogger _logger; public ResultadosController(EleccionesDbContext dbContext, ILogger logger) { _dbContext = dbContext; _logger = logger; } [HttpGet("municipio/{municipioId}")] public async Task GetResultadosPorMunicipio(string municipioId) { // 1. Buscamos el ámbito geográfico correspondiente al municipio var ambito = await _dbContext.AmbitosGeograficos .AsNoTracking() .FirstOrDefaultAsync(a => a.MunicipioId == municipioId); if (ambito == null) { return NotFound(new { message = $"No se encontró el municipio con ID {municipioId}" }); } // 2. Buscamos el estado del recuento para ese ámbito var estadoRecuento = await _dbContext.EstadosRecuentos .AsNoTracking() .FirstOrDefaultAsync(e => e.AmbitoGeograficoId == ambito.Id); if (estadoRecuento == null) { return NotFound(new { message = $"No se han encontrado resultados para el municipio {ambito.Nombre}" }); } // 3. Buscamos todos los votos para ese ámbito, incluyendo el nombre de la agrupación var resultadosVotos = await _dbContext.ResultadosVotos .AsNoTracking() .Include(rv => rv.AgrupacionPolitica) // ¡Crucial para obtener el nombre del partido! .Where(rv => rv.AmbitoGeograficoId == ambito.Id) .ToListAsync(); // 4. Calculamos el total de votos positivos para el porcentaje long totalVotosPositivos = resultadosVotos.Sum(r => r.CantidadVotos); // 5. Mapeamos todo a nuestro DTO de respuesta var respuestaDto = new MunicipioResultadosDto { MunicipioNombre = ambito.Nombre, UltimaActualizacion = estadoRecuento.FechaTotalizacion, PorcentajeEscrutado = estadoRecuento.MesasTotalizadas * 100.0m / (estadoRecuento.MesasEsperadas > 0 ? estadoRecuento.MesasEsperadas : 1), PorcentajeParticipacion = estadoRecuento.ParticipacionPorcentaje, Resultados = resultadosVotos.Select(rv => new AgrupacionResultadoDto { Nombre = rv.AgrupacionPolitica.Nombre, Votos = rv.CantidadVotos, Porcentaje = totalVotosPositivos > 0 ? (rv.CantidadVotos * 100.0m / totalVotosPositivos) : 0 }).OrderByDescending(r => r.Votos).ToList(), VotosAdicionales = new VotosAdicionalesDto { EnBlanco = estadoRecuento.VotosEnBlanco, Nulos = estadoRecuento.VotosNulos, Recurridos = estadoRecuento.VotosRecurridos } }; // Devolvemos el resultado return Ok(respuestaDto); } [HttpGet("provincia/{distritoId}")] public async Task GetResultadosProvinciales(string distritoId) { // TODO: Esta lógica debe ser reemplazada para leer datos reales de la BD // cuando el worker comience a ingestar los totales a nivel provincial. // Por ahora, devolvemos datos simulados para permitir el desarrollo del frontend. var ambito = await _dbContext.AmbitosGeograficos.AsNoTracking() .FirstOrDefaultAsync(a => a.DistritoId == distritoId && a.NivelId == 10); if (ambito == null) { return NotFound(new { message = "No se encontró la provincia" }); } // Simulación var random = new Random(); var respuestaSimulada = new ResumenProvincialDto { ProvinciaNombre = ambito.Nombre, UltimaActualizacion = DateTime.UtcNow, PorcentajeEscrutado = 78.45m, PorcentajeParticipacion = 65.12m, Resultados = [ new() { Nombre = "ALIANZA POR EL FUTURO", Votos = 2500000 + random.Next(1, 1000), Porcentaje = 45.12m }, new() { Nombre = "FRENTE DE AVANZADA", Votos = 2100000 + random.Next(1, 1000), Porcentaje = 38.78m }, new() { Nombre = "UNION POPULAR", Votos = 800000 + random.Next(1, 1000), Porcentaje = 14.10m }, ], VotosAdicionales = new VotosAdicionalesDto { EnBlanco = 150000, Nulos = 80000, Recurridos = 1200 } }; return Ok(await Task.FromResult(respuestaSimulada)); } }