package dev.mvvasilev.finances.services; import dev.mvvasilev.common.data.AbstractEntity; import dev.mvvasilev.finances.dtos.CategoryDTO; import dev.mvvasilev.finances.dtos.SpendingByCategoriesDTO; import dev.mvvasilev.finances.dtos.SpendingByCategoryDTO; import dev.mvvasilev.finances.dtos.SpendingOverTimeByCategoryDTO; import dev.mvvasilev.finances.enums.TimePeriod; import dev.mvvasilev.finances.persistence.StatisticsRepository; import dev.mvvasilev.finances.persistence.TransactionCategoryRepository; import org.springframework.stereotype.Service; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.*; @Service public class StatisticsService { private final TransactionCategoryRepository transactionCategoryRepository; private final StatisticsRepository statisticsRepository; public StatisticsService(TransactionCategoryRepository transactionCategoryRepository, StatisticsRepository statisticsRepository) { this.transactionCategoryRepository = transactionCategoryRepository; this.statisticsRepository = statisticsRepository; } public SpendingByCategoriesDTO spendingByCategory(Long[] categoryId, LocalDateTime from, LocalDateTime to) { final var categories = transactionCategoryRepository.findAllById(Arrays.stream(categoryId).toList()).stream().map(c -> new CategoryDTO(c.getId(), c.getName(), null)).toList(); final var spendingByCategory = statisticsRepository.fetchSpendingByCategory( categoryId, from, to ); return new SpendingByCategoriesDTO( categories, spendingByCategory ); } public SpendingOverTimeByCategoryDTO spendingByCategoryOverTime( Long[] categoryId, TimePeriod period, LocalDateTime from, LocalDateTime to ) { return new SpendingOverTimeByCategoryDTO( transactionCategoryRepository.findAllById(Arrays.stream(categoryId).toList()).stream().map(c -> new CategoryDTO(c.getId(), c.getName(), null)).toList(), period.getDuration(), statisticsRepository.fetchSpendingByCategoryOverTime(from, to, period, categoryId).stream().map(dto -> new SpendingByCategoryDTO( dto.categoryId(), dto.periodBeginningTimestamp(), dto.amountForPeriod() )).toList() ); } // Impose limits if necessary private boolean validatePeriodWithinLimits(LocalDateTime from, LocalDateTime to, TimePeriod period) { return switch (period) { // Can't request daily spending breakdown for a period longer than a month case DAILY -> ChronoUnit.MONTHS.between(from, to) <= 1; // Can't request weekly spending breakdown for a period longer than a year case WEEKLY -> ChronoUnit.YEARS.between(from, to) <= 1; // Can't request bi-weekly spending breakdown for a period longer than a year case BIWEEKLY -> ChronoUnit.YEARS.between(from, to) <= 1; // Can't request monthly spending breakdown for a period longer than 3 years case MONTHLY -> ChronoUnit.YEARS.between(from, to) <= 3; // Can't request quarterly spending breakdown for a period longer than 5 years case QUARTERLY -> ChronoUnit.YEARS.between(from, to) <= 5; // Can't request yearly spending breakdown for a period longer than 30 years case YEARLY -> ChronoUnit.YEARS.between(from, to) <= 30; }; } public Double sumByCategory(Long[] categoryId, LocalDateTime from, LocalDateTime to, Boolean includeUncategorized) { return statisticsRepository.sumByCategory(categoryId, from, to, includeUncategorized); } }