using System.Threading; using System.Threading.Tasks; using GermanApp.Application.DTOs; using GermanApp.Domain.Entities; using GermanApp.Domain.Interfaces; using Microsoft.Extensions.Logging; namespace GermanApp.Application.Services; /// /// Application service for managing story segment unlocking. /// This service is called when a user completes a lesson to unlock the next story segment. /// This is part of the Application layer. /// public class StoryUnlockService { private readonly StoryService _storyService; private readonly IUserProgressRepository _userProgressRepository; private readonly ILessonRepository _lessonRepository; private readonly ILogger _logger; /// /// Creates a new StoryUnlockService. /// /// Service for story segment operations /// Repository for user progress /// Repository for lessons /// Logger for service operations public StoryUnlockService( StoryService storyService, IUserProgressRepository userProgressRepository, ILessonRepository lessonRepository, ILogger logger) { _storyService = storyService; _userProgressRepository = userProgressRepository; _lessonRepository = lessonRepository; _logger = logger; } /// /// Called when a user completes a lesson. /// Checks if the user should unlock a new story segment and does so. /// /// The user ID /// The lesson ID that was completed /// Cancellation token /// True if a new segment was unlocked, false otherwise public virtual async Task HandleLessonCompletionAsync( int userId, int lessonId, CancellationToken cancellationToken = default) { _logger.LogInformation( "Handling lesson completion for user {UserId}, lesson {LessonId}", userId, lessonId); // Get the lesson to find its level and order var lesson = await _lessonRepository.GetByIdAsync(lessonId, cancellationToken); if (lesson == null) { _logger.LogWarning("Lesson not found: {LessonId}", lessonId); return false; } // Check if this lesson completion qualifies for unlocking a story segment // We need to check if the user has actually completed this lesson var isCompleted = await _userProgressRepository.HasUserCompletedLessonAsync( userId, lessonId, cancellationToken); if (!isCompleted) { _logger.LogInformation( "Lesson {LessonId} not marked as completed for user {UserId}", lessonId, userId); return false; } // Get the next story segment to unlock var nextSegment = await _storyService.GetNextSegmentToUnlockAsync( lesson.LevelId, lesson.Order, cancellationToken); if (nextSegment == null) { _logger.LogInformation( "No story segment to unlock for user {UserId} after lesson {LessonId}", userId, lessonId); return false; } // Check if user already has this segment unlocked var alreadyUnlocked = await _storyService.IsSegmentUnlockedAsync( userId, nextSegment.Id, cancellationToken); if (alreadyUnlocked) { _logger.LogInformation( "Segment {SegmentId} already unlocked for user {UserId}", nextSegment.Id, userId); return false; } // Unlock the segment var unlockedSegment = await _storyService.UnlockNextSegmentAsync( userId, lesson.LevelId, lesson.Order, cancellationToken); if (unlockedSegment != null) { _logger.LogInformation( "Unlocked story segment {SegmentId} for user {UserId} after completing lesson {LessonId}", unlockedSegment.Id, userId, lessonId); return true; } return false; } /// /// Checks if a user has unlocked a specific story segment. /// /// The user ID /// The segment ID /// Cancellation token /// True if the segment is unlocked public virtual async Task IsSegmentUnlockedAsync( int userId, int segmentId, CancellationToken cancellationToken = default) { return await _storyService.IsSegmentUnlockedAsync(userId, segmentId, cancellationToken); } /// /// Checks if a user has completed (read/listened to) a specific story segment. /// /// The user ID /// The segment ID /// Cancellation token /// True if the segment is completed public virtual async Task IsSegmentCompletedAsync( int userId, int segmentId, CancellationToken cancellationToken = default) { return await _storyService.IsSegmentCompletedAsync(userId, segmentId, cancellationToken); } /// /// Marks a story segment as completed for a user. /// /// The user ID /// The segment ID /// Cancellation token /// True if the segment was marked as completed public virtual async Task MarkSegmentAsCompletedAsync( int userId, int segmentId, CancellationToken cancellationToken = default) { return await _storyService.MarkSegmentAsCompletedAsync(userId, segmentId, cancellationToken); } /// /// Gets a user's story progress for a level. /// /// The user ID /// The level ID /// Cancellation token /// Story progress DTO public virtual async Task GetUserProgressAsync( int userId, int levelId, CancellationToken cancellationToken = default) { return await _storyService.GetUserProgressAsync(userId, levelId, cancellationToken); } }