using BudgetApp.Data; namespace BudgetApp.Services; public class DashboardService { private readonly TransactionService _transactionService; private readonly AccountService _accountService; private readonly CategoryService _categoryService; public DashboardService(TransactionService transactionService, AccountService accountService, CategoryService categoryService) { _transactionService = transactionService; _accountService = accountService; _categoryService = categoryService; } public async Task GetDashboardDataAsync(string userId, DateTime? from = null, DateTime? to = null) { await _categoryService.EnsureDefaultCategoriesAsync(userId); var windowEnd = to ?? DateTime.Today.AddDays(30); await _transactionService.MaterializeRecurringTransactionsAsync(userId, windowEnd); var accounts = await _accountService.GetAccountsAsync(userId); var categories = await _categoryService.GetCategoriesAsync(userId); var windowStart = from ?? DateTime.Today.AddDays(-30); var fetchStart = windowStart < DateTime.Today ? windowStart : DateTime.Today; var fetchEnd = windowEnd > DateTime.Today ? windowEnd : DateTime.Today; var allTransactions = await _transactionService.GetTransactionsInRangeAsync(userId, fetchStart, fetchEnd); var recent = allTransactions .Where(t => t.Date.Date >= windowStart.Date && t.Date.Date <= DateTime.Today) .OrderByDescending(t => t.Date) .ToList(); var upcoming = allTransactions .Where(t => t.Date.Date > DateTime.Today && t.Date.Date <= windowEnd.Date) .OrderBy(t => t.Date) .ToList(); var chartData = BuildChartData(accounts, allTransactions, windowStart, windowEnd); return new DashboardData { Accounts = accounts, Categories = categories, RecentTransactions = recent, UpcomingTransactions = upcoming, ChartData = chartData, CurrencySymbol = accounts.FirstOrDefault()?.GetSymbol() ?? "£" }; } private static ChartData BuildChartData(List accounts, List transactions, DateTime startDate, DateTime endDate) { var today = DateTime.Today; var labels = new List(); var datasets = new List(); var dayTransactionLabels = new List>(); for (var d = startDate; d <= endDate; d = d.AddDays(1)) { labels.Add(d.ToString("MMM dd")); var dayTxns = transactions .Where(t => t.Date.Date == d.Date) .Select(t => $"{(t.Type == TransactionType.Income ? "+" : "-")}{t.Account.FormatAmount(t.Amount)} {t.Description} ({t.Account.Name})") .ToList(); dayTransactionLabels.Add(dayTxns); } foreach (var account in accounts) { var accountTransactions = transactions .Where(t => t.AccountId == account.Id) .OrderBy(t => t.Date) .ToList(); var dataPoints = new List(); var runningBalance = account.CurrentBalance; if (startDate.Date <= today) { foreach (var t in accountTransactions .Where(t => t.Date.Date >= startDate.Date && t.Date.Date <= today) .OrderByDescending(t => t.Date)) { runningBalance -= t.Type == TransactionType.Income ? t.Amount : -t.Amount; } } else { foreach (var t in accountTransactions .Where(t => t.Date.Date > today && t.Date.Date < startDate.Date) .OrderBy(t => t.Date)) { runningBalance += t.Type == TransactionType.Income ? t.Amount : -t.Amount; } } for (var d = startDate; d <= endDate; d = d.AddDays(1)) { var dayTxns = accountTransactions.Where(t => t.Date.Date == d.Date); foreach (var t in dayTxns) { runningBalance += t.Type == TransactionType.Income ? t.Amount : -t.Amount; } dataPoints.Add(runningBalance); } datasets.Add(new ChartDataset { Label = account.Name, Color = account.Color, Data = dataPoints, CurrencySymbol = account.GetSymbol() }); } return new ChartData { Labels = labels, Datasets = datasets, DayTransactionLabels = dayTransactionLabels }; } } public class DashboardData { public List Accounts { get; set; } = []; public List Categories { get; set; } = []; public List RecentTransactions { get; set; } = []; public List UpcomingTransactions { get; set; } = []; public ChartData ChartData { get; set; } = new(); public string CurrencySymbol { get; set; } = "$"; } public class ChartData { public List Labels { get; set; } = []; public List Datasets { get; set; } = []; public List> DayTransactionLabels { get; set; } = []; } public class ChartDataset { public string Label { get; set; } = string.Empty; public string Color { get; set; } = "#1b6ec2"; public List Data { get; set; } = []; public string CurrencySymbol { get; set; } = "$"; }