using Budget.Api.Services; using Budget.Core.DTOs; using Budget.Core.Models; using Budget.Core.Services; using Budget.Infrastructure.Data; using Budget.Infrastructure.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace Budget.Api.Controllers; [ApiController] [Route("api/budgets/{budgetId:guid}/summary")] [Authorize(Roles = "admin,user")] public class SummaryController(AppDbContext db, BudgetAuthorizationService authz) : ControllerBase { private IActionResult? TryGetUserId(out string userId) { var id = User.GetUserId(); if (id is null) { userId = string.Empty; return Unauthorized(); } userId = id; return null; } [HttpGet] public async Task Get(Guid budgetId) { if (TryGetUserId(out var userId) is { } err) return err; if (!await authz.CanReadAsync(budgetId, userId)) return Forbid(); var budget = await db.Budgets.AsNoTracking().FirstOrDefaultAsync(b => b.Id == budgetId); if (budget is null) return NotFound(); var incomes = await db.Incomes.AsNoTracking().Where(i => i.BudgetId == budgetId).ToListAsync(); var outgos = await db.Outgos.AsNoTracking().Where(o => o.BudgetId == budgetId).ToListAsync(); var monthlyIncome = incomes.Sum(i => FrequencyCalculator.ToMonthly(i.Amount, i.Frequency)); var annualIncome = monthlyIncome * 12m; var typeTargets = new Dictionary { [OutgoType.Need] = 50, [OutgoType.Want] = 30, [OutgoType.Save] = 20, }; var breakdown = new List(); decimal totalMonthlyOutgo = 0m; foreach (var (type, target) in typeTargets) { var typeOutgos = outgos.Where(o => o.Type == type).ToList(); var monthlyTotal = typeOutgos.Sum(o => FrequencyCalculator.ToMonthly(o.Amount, o.Frequency)); var annualTotal = monthlyTotal * 12m; totalMonthlyOutgo += monthlyTotal; var percent = monthlyIncome > 0 ? Math.Round(monthlyTotal / monthlyIncome * 100, 2) : 0m; var maxAmount = monthlyIncome * target / 100m; var remaining = maxAmount - monthlyTotal; breakdown.Add(new SummaryBreakdownItem(type.ToString(), target, monthlyTotal, annualTotal, percent, maxAmount, remaining)); } var unspent = monthlyIncome - totalMonthlyOutgo; var unspentPercent = monthlyIncome > 0 ? Math.Round(unspent / monthlyIncome * 100, 2) : 0m; breakdown.Add(new SummaryBreakdownItem("Unspent", null, unspent, unspent * 12m, unspentPercent, null, null)); return Ok(new SummaryDto(monthlyIncome, annualIncome, breakdown)); } }