using FlowAPI.Application.DTOs.Habit; using FlowAPI.Application.Interfaces; using FlowAPI.Domain.Entities; namespace FlowAPI.Application.Services { public class HabitService : IHabitService { private readonly IHabitRecordRepository _repo; private readonly IUserRepository _userRepo; private readonly IAchievementService _achievementService; public HabitService(IHabitRecordRepository repo, IUserRepository userRepo, IAchievementService achievementService) { _repo = repo; _userRepo = userRepo; _achievementService = achievementService; } public async Task> GetAllByUserIdAsync(Guid userId) { var records = await _repo.GetAllByUserIdAsync(userId); return records.Select(MapToDto); } public async Task RecordHabitAsync(CreateHabitRecordDto dto) { var user = await _userRepo.GetByIdAsync(dto.UserId); if (user == null) throw new Exception("User not found."); var targetDate = dto.CheckedDate ?? DateTime.UtcNow; var existing = await _repo.GetRecordForDateAsync(dto.UserId, dto.HabitName, targetDate.Date); if (existing is not null) return MapToDto(existing); var tier = user.SubscriptionTier?.Trim(); bool isFree = string.IsNullOrEmpty(tier) || tier == "Free"; if (isFree) { var allRecords = await _repo.GetAllByUserIdAsync(dto.UserId); var distinctHabits = allRecords.Select(r => r.HabitName).Distinct().ToList(); if (!distinctHabits.Contains(dto.HabitName) && distinctHabits.Count >= 3) { throw new InvalidOperationException("Free tier is limited to 3 habits."); } } var record = new HabitRecord { Id = Guid.NewGuid(), UserId = dto.UserId, HabitName = dto.HabitName, CheckedDate = targetDate }; var created = await _repo.CreateAsync(record); await _achievementService.ProcessEventAsync(dto.UserId, "HabitTracked"); return MapToDto(created); } public async Task GetStreakAsync(Guid userId, string habitName) { var records = await _repo.GetAllByUserIdAsync(userId); var habitRecords = records .Where(r => r.HabitName == habitName) .OrderByDescending(r => r.CheckedDate) .ToList(); if (!habitRecords.Any()) return 0; int streak = 0; DateTime expectedDate = DateTime.UtcNow.Date; foreach (var record in habitRecords) { if (record.CheckedDate.Date == expectedDate) { streak++; expectedDate = expectedDate.AddDays(-1); } else if (record.CheckedDate.Date > expectedDate) { continue; } else { break; } } return streak; } private static HabitRecordResponseDto MapToDto(HabitRecord record) => new() { Id = record.Id, UserId = record.UserId, HabitName = record.HabitName, CheckedDate = record.CheckedDate }; } }