DEV Community

John
John

Posted on Edited on

Refresh Token interceptor

import { HttpErrorResponse, HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from '../services/auth-service';
import { catchError, switchMap, throwError } from 'rxjs';

export const authInterceptorInterceptor: HttpInterceptorFn = (req, next) => {
// const toaster=inject(ToastrService);
const authService=inject(AuthService);
const token=localStorage.getItem("access_token");

// token attach with request
public interface IInstructorService
{
Task> GetAllAsync();
Task GetByIdAsync(int id);
Task AddAsync(Instructor instructor);
Task UpdateAsync(Instructor instructor);
Task DeleteAsync(int id);
}

public interface ICategoryService
{
Task> GetAllAsync();
Task GetByIdAsync(int id);
}

public interface ICourseService
{
Task> GetAllAsync();
Task GetByIdAsync(int id);
Task AddAsync(Course course);
Task UpdateAsync(Course course);
Task DeleteAsync(int id);
}

public interface ILessonService
{
Task> GetAllAsync();
Task GetByIdAsync(int id);
Task> GetByCourseIdAsync(int courseId);
Task AddAsync(Lesson lesson);
Task UpdateAsync(Lesson lesson);
Task DeleteAsync(int id);
}

public interface IEnrollmentService
{
Task> GetAllAsync();
Task GetByIdAsync(int id);
Task> GetByUserIdAsync(string userId);
Task AddAsync(Enrollment enrollment);
Task UpdateAsync(Enrollment enrollment);
Task CancelAsync(int id);
}

public interface ILessonProgressService
{
Task GetByIdAsync(int id);
Task> GetByEnrollmentIdAsync(int enrollmentId);
Task AddAsync(LessonProgress progress);
Task UpdateAsync(LessonProgress progress);
}

public interface IReviewService
{
Task> GetByCourseIdAsync(int courseId);
Task GetByIdAsync(int id);
Task AddAsync(Review review);
Task UpdateAsync(Review review);
Task DeleteAsync(int id);
}

public class InstructorService : IInstructorService
{
private readonly IInstructorRepository _instructorRepository;
private readonly ICourseRepository _courseRepository;

public InstructorService(
    IInstructorRepository instructorRepository,
    ICourseRepository courseRepository)
{
    _instructorRepository = instructorRepository;
    _courseRepository = courseRepository;
}

public async Task<List<Instructor>> GetAllAsync()
{
    var instructors = await _instructorRepository.GetAllAsync();

    return instructors
        .Where(x => !x.IsDeleted)
        .ToList();
}

public async Task<Instructor?> GetByIdAsync(int id)
{
    var instructor = await _instructorRepository.GetByIdAsync(id);

    if (instructor == null || instructor.IsDeleted)
        return null;

    return instructor;
}

public async Task AddAsync(Instructor instructor)
{
    var instructors = await _instructorRepository.GetAllAsync();

    // Unique Email
    bool emailExists = instructors.Any(x =>
        !x.IsDeleted &&
        x.Email.Equals(
            instructor.Email,
            StringComparison.OrdinalIgnoreCase));

    if (emailExists)
        throw new Exception("Instructor email already exists.");

    // Auto Generate Instructor Code
    instructor.InstructorCode =
        GenerateInstructorCode(instructors);

    // Audit fields
    instructor.CreatedDate = DateTime.UtcNow;
    instructor.CreatedBy = "System";

    instructor.IsDeleted = false;
    instructor.DeletedBy = null;
    instructor.DeletedDate = null;

    await _instructorRepository.AddAsync(instructor);

    await _instructorRepository.SaveAsync();
}

public async Task UpdateAsync(Instructor instructor)
{
    var existingInstructor =
        await _instructorRepository.GetByIdAsync(instructor.Id);

    if (existingInstructor == null ||
        existingInstructor.IsDeleted)
    {
        throw new Exception("Instructor not found.");
    }

    var instructors = await _instructorRepository.GetAllAsync();

    // Unique Email
    bool emailExists = instructors.Any(x =>
        x.Id != instructor.Id &&
        !x.IsDeleted &&
        x.Email.Equals(
            instructor.Email,
            StringComparison.OrdinalIgnoreCase));

    if (emailExists)
        throw new Exception("Instructor email already exists.");

    // Don't change InstructorCode
    existingInstructor.InstructorName =
        instructor.InstructorName;

    existingInstructor.Email =
        instructor.Email;

    existingInstructor.Phone =
        instructor.Phone;

    existingInstructor.Expertise =
        instructor.Expertise;

    existingInstructor.Qualifications =
        instructor.Qualifications;

    existingInstructor.YearsOfExperience =
        instructor.YearsOfExperience;

    existingInstructor.Status =
        instructor.Status;

    // Audit fields
    existingInstructor.UpdatedDate =
        DateTime.UtcNow;

    existingInstructor.UpdatedBy =
        "System";

    _instructorRepository.Update(existingInstructor);

    await _instructorRepository.SaveAsync();
}

public async Task DeleteAsync(int id)
{
    var instructor =
        await _instructorRepository.GetByIdAsync(id);

    if (instructor == null ||
        instructor.IsDeleted)
    {
        throw new Exception("Instructor not found.");
    }

    // Business Rule:
    // Cannot delete instructor with active courses
    var courses =
        await _courseRepository.GetAllAsync();

    bool hasActiveCourses = courses.Any(x =>
        x.InstructorId == id &&
        !x.IsDeleted &&
        x.Status == "Active");

    if (hasActiveCourses)
    {
        throw new Exception(
            "Cannot delete instructor because instructor has active courses.");
    }

    // Soft Delete
    instructor.IsDeleted = true;
    instructor.DeletedBy = "System";
    instructor.DeletedDate = DateTime.UtcNow;

    _instructorRepository.Update(instructor);

    await _instructorRepository.SaveAsync();
}

private string GenerateInstructorCode(
    List<Instructor> instructors)
{
    int maxNumber = 0;

    foreach (var instructor in instructors)
    {
        if (instructor.InstructorCode.StartsWith("INS"))
        {
            var numberPart =
                instructor.InstructorCode.Substring(3);

            if (int.TryParse(numberPart, out int number))
            {
                if (number > maxNumber)
                    maxNumber = number;
            }
        }
    }

    return $"INS{(maxNumber + 1):D4}";
}
Enter fullscreen mode Exit fullscreen mode

}

public class CategoryService : ICategoryService
{
private readonly ICategoryRepository _categoryRepository;

public CategoryService(ICategoryRepository categoryRepository)
{
    _categoryRepository = categoryRepository;
}

public async Task<List<Category>> GetAllAsync()
{
    return await _categoryRepository.GetAllAsync();
}

public async Task<Category?> GetByIdAsync(int id)
{
    if (id <= 0)
        throw new ArgumentException("Invalid category id.");

    return await _categoryRepository.GetByIdAsync(id);
}
Enter fullscreen mode Exit fullscreen mode

}

public class CourseService : ICourseService
{
private readonly ICourseRepository _courseRepository;
private readonly IInstructorRepository _instructorRepository;
private readonly ICategoryRepository _categoryRepository;
private readonly IEnrollmentRepository _enrollmentRepository;

public CourseService(
    ICourseRepository courseRepository,
    IInstructorRepository instructorRepository,
    ICategoryRepository categoryRepository,
    IEnrollmentRepository enrollmentRepository)
{
    _courseRepository = courseRepository;
    _instructorRepository = instructorRepository;
    _categoryRepository = categoryRepository;
    _enrollmentRepository = enrollmentRepository;
}

public async Task<List<Course>> GetAllAsync()
{
    var courses = await _courseRepository.GetAllAsync();

    return courses
        .Where(c => !c.IsDeleted)
        .ToList();
}

public async Task<Course?> GetByIdAsync(int id)
{
    if (id <= 0)
        throw new ArgumentException("Invalid course id.");

    var course = await _courseRepository.GetByIdAsync(id);

    if (course == null || course.IsDeleted)
        return null;

    return course;
}

public async Task AddAsync(Course course)
{
    // Category must exist
    var category = await _categoryRepository
        .GetByIdAsync(course.CategoryId);

    if (category == null)
        throw new Exception("Category not found.");

    // Instructor must exist
    var instructor = await _instructorRepository
        .GetByIdAsync(course.InstructorId);

    if (instructor == null || instructor.IsDeleted)
        throw new Exception("Instructor not found.");

    // Business Rule:
    // Inactive instructors cannot create new courses
    if (instructor.Status != "Active")
        throw new Exception(
            "Inactive instructor cannot create a course.");

    // Business Rule:
    // Start date must be in future
    if (course.StartDate <= DateTime.UtcNow)
        throw new Exception(
            "Course start date must be in the future.");

    // Auto-generate Course Code
    var courses = await _courseRepository.GetAllAsync();

    course.CourseCode = GenerateCourseCode(courses);

    // Default values
    course.IsDeleted = false;

    if (string.IsNullOrWhiteSpace(course.Status))
        course.Status = "Draft";

    // Audit fields
    course.CreatedDate = DateTime.UtcNow;
    course.CreatedBy = "System";

    course.UpdatedDate = null;
    course.UpdatedBy = null;

    course.DeletedDate = null;
    course.DeletedBy = null;

    await _courseRepository.AddAsync(course);

    await _courseRepository.SaveAsync();
}

public async Task UpdateAsync(Course course)
{
    var existingCourse =
        await _courseRepository.GetByIdAsync(course.Id);

    if (existingCourse == null ||
        existingCourse.IsDeleted)
    {
        throw new Exception("Course not found.");
    }

    // Category must exist
    var category = await _categoryRepository
        .GetByIdAsync(course.CategoryId);

    if (category == null)
        throw new Exception("Category not found.");

    // Instructor must exist
    var instructor = await _instructorRepository
        .GetByIdAsync(course.InstructorId);

    if (instructor == null || instructor.IsDeleted)
        throw new Exception("Instructor not found.");

    // Business Rule:
    // Inactive instructor cannot be assigned to a new/updated course
    if (instructor.Status != "Active")
        throw new Exception(
            "Inactive instructor cannot be assigned to a course.");

    // Business Rule:
    // Start date cannot be in the past
    if (course.StartDate <= DateTime.UtcNow)
        throw new Exception(
            "Course start date must be in the future.");

    // Course Code should not be changed
    existingCourse.CourseTitle = course.CourseTitle;

    existingCourse.CategoryId = course.CategoryId;

    existingCourse.InstructorId = course.InstructorId;

    existingCourse.Description = course.Description;

    existingCourse.DurationHours = course.DurationHours;

    existingCourse.Price = course.Price;

    existingCourse.MaxStudents = course.MaxStudents;

    existingCourse.StartDate = course.StartDate;

    existingCourse.Status = course.Status;

    // Audit fields
    existingCourse.UpdatedDate = DateTime.UtcNow;
    existingCourse.UpdatedBy = "System";

    _courseRepository.Update(existingCourse);

    await _courseRepository.SaveAsync();
}

public async Task DeleteAsync(int id)
{
    var course =
        await _courseRepository.GetByIdAsync(id);

    if (course == null || course.IsDeleted)
        throw new Exception("Course not found.");

    // Business Rule:
    // Cannot delete course with active student enrollments
    var enrollments =
        await _enrollmentRepository.GetAllAsync();

    bool hasActiveEnrollments = enrollments.Any(e =>
        e.CourseId == id &&
        (e.Status == "Enrolled" ||
         e.Status == "Active"));

    if (hasActiveEnrollments)
    {
        throw new Exception(
            "Cannot delete course because it has active student enrollments.");
    }

    // Soft Delete
    course.IsDeleted = true;
    course.DeletedDate = DateTime.UtcNow;
    course.DeletedBy = "System";

    course.UpdatedDate = DateTime.UtcNow;
    course.UpdatedBy = "System";

    _courseRepository.Update(course);

    await _courseRepository.SaveAsync();
}

private string GenerateCourseCode(List<Course> courses)
{
    int maxNumber = 0;

    foreach (var course in courses)
    {
        if (string.IsNullOrWhiteSpace(course.CourseCode))
            continue;

        if (course.CourseCode.StartsWith("CRS"))
        {
            var numberPart =
                course.CourseCode.Substring(3);

            if (int.TryParse(
                numberPart,
                out int number))
            {
                if (number > maxNumber)
                    maxNumber = number;
            }
        }
    }

    return $"CRS{(maxNumber + 1):D4}";
}
Enter fullscreen mode Exit fullscreen mode

}

public class LessonService : ILessonService
{
private readonly ILessonRepository _lessonRepository;
private readonly ICourseRepository _courseRepository;

public LessonService(
    ILessonRepository lessonRepository,
    ICourseRepository courseRepository)
{
    _lessonRepository = lessonRepository;
    _courseRepository = courseRepository;
}

public async Task<List<Lesson>> GetAllAsync()
{
    return await _lessonRepository.GetAllAsync();
}

public async Task<Lesson?> GetByIdAsync(int id)
{
    if (id <= 0)
        throw new ArgumentException("Invalid lesson id.");

    return await _lessonRepository.GetByIdAsync(id);
}

public async Task<List<Lesson>> GetByCourseIdAsync(int courseId)
{
    if (courseId <= 0)
        throw new ArgumentException("Invalid course id.");

    var course = await _courseRepository.GetByIdAsync(courseId);

    if (course == null || course.IsDeleted)
        throw new Exception("Course not found.");

    return await _lessonRepository.GetByCourseIdAsync(courseId);
}

public async Task AddAsync(Lesson lesson)
{
    // Course must exist
    var course = await _courseRepository
        .GetByIdAsync(lesson.CourseId);

    if (course == null || course.IsDeleted)
        throw new Exception("Course not found.");

    // Check duplicate lesson order
    var lessons = await _lessonRepository
        .GetByCourseIdAsync(lesson.CourseId);

    bool orderExists = lessons.Any(x =>
        x.LessonOrder == lesson.LessonOrder);

    if (orderExists)
        throw new Exception(
            "This lesson order already exists for the course.");

    await _lessonRepository.AddAsync(lesson);

    await _lessonRepository.SaveAsync();
}

public async Task UpdateAsync(Lesson lesson)
{
    var existingLesson =
        await _lessonRepository.GetByIdAsync(lesson.Id);

    if (existingLesson == null)
        throw new Exception("Lesson not found.");

    // Course must exist
    var course = await _courseRepository
        .GetByIdAsync(lesson.CourseId);

    if (course == null || course.IsDeleted)
        throw new Exception("Course not found.");

    // Check duplicate lesson order
    var lessons = await _lessonRepository
        .GetByCourseIdAsync(lesson.CourseId);

    bool orderExists = lessons.Any(x =>
        x.Id != lesson.Id &&
        x.LessonOrder == lesson.LessonOrder);

    if (orderExists)
        throw new Exception(
            "This lesson order already exists for the course.");

    existingLesson.CourseId = lesson.CourseId;
    existingLesson.Title = lesson.Title;
    existingLesson.Description = lesson.Description;
    existingLesson.LessonOrder = lesson.LessonOrder;
    existingLesson.EstimatedDuration =
        lesson.EstimatedDuration;

    _lessonRepository.Update(existingLesson);

    await _lessonRepository.SaveAsync();
}

public async Task DeleteAsync(int id)
{
    var lesson =
        await _lessonRepository.GetByIdAsync(id);

    if (lesson == null)
        throw new Exception("Lesson not found.");

    _lessonRepository.Delete(lesson);

    await _lessonRepository.SaveAsync();
}
Enter fullscreen mode Exit fullscreen mode

}

public class EnrollmentService : IEnrollmentService
{
private readonly IEnrollmentRepository _enrollmentRepository;
private readonly ICourseRepository _courseRepository;

public EnrollmentService(
    IEnrollmentRepository enrollmentRepository,
    ICourseRepository courseRepository)
{
    _enrollmentRepository = enrollmentRepository;
    _courseRepository = courseRepository;
}

public async Task<List<Enrollment>> GetAllAsync()
{
    return await _enrollmentRepository.GetAllAsync();
}

public async Task<Enrollment?> GetByIdAsync(int id)
{
    if (id <= 0)
        throw new ArgumentException("Invalid enrollment id.");

    return await _enrollmentRepository.GetByIdAsync(id);
}

public async Task<List<Enrollment>> GetByUserIdAsync(string userId)
{
    if (string.IsNullOrWhiteSpace(userId))
        throw new ArgumentException("User id is required.");

    return await _enrollmentRepository.GetByUserIdAsync(userId);
}

public async Task AddAsync(Enrollment enrollment)
{
    // Course must exist
    var course = await _courseRepository
        .GetByIdAsync(enrollment.CourseId);

    if (course == null || course.IsDeleted)
        throw new Exception("Course not found.");

    // Course must be Active
    if (course.Status != "Active")
        throw new Exception(
            "Enrollment is allowed only for active courses.");

    // Course Start Date must not have passed
    if (course.StartDate <= DateTime.UtcNow)
        throw new Exception(
            "Cannot enroll because the course has already started.");

    // Terms and Conditions must be accepted
    if (!enrollment.AgreeToTerms)
        throw new Exception(
            "You must agree to the Terms and Conditions.");

    // User cannot enroll more than once in same course
    var userEnrollments =
        await _enrollmentRepository
            .GetByUserIdAsync(enrollment.UserId);

    bool alreadyEnrolled = userEnrollments.Any(x =>
        x.CourseId == enrollment.CourseId &&
        x.Status != "Dropped");

    if (alreadyEnrolled)
        throw new Exception(
            "You are already enrolled in this course.");

    // User cannot have more than 20 active enrollments
    int activeEnrollments = userEnrollments.Count(x =>
        x.Status == "Enrolled" ||
        x.Status == "Active");

    if (activeEnrollments >= 20)
        throw new Exception(
            "You cannot have more than 20 active course enrollments.");

    // Course capacity check
    int currentStudents = await GetCurrentStudentCountAsync(
        enrollment.CourseId);

    if (currentStudents >= course.MaxStudents)
        throw new Exception(
            "Course has reached its maximum student capacity.");

    // Auto-set Enrollment Date
    enrollment.EnrollmentDate = DateTime.UtcNow;

    // Auto-set Status
    enrollment.Status = "Enrolled";

    await _enrollmentRepository.AddAsync(enrollment);

    await _enrollmentRepository.SaveAsync();
}

public async Task UpdateAsync(Enrollment enrollment)
{
    var existingEnrollment =
        await _enrollmentRepository.GetByIdAsync(enrollment.Id);

    if (existingEnrollment == null)
        throw new Exception("Enrollment not found.");

    // Status flow:
    // Enrolled → Active → Completed/Dropped

    ValidateStatusTransition(
        existingEnrollment.Status,
        enrollment.Status);

    existingEnrollment.Status = enrollment.Status;

    existingEnrollment.SpecialRequirements =
        enrollment.SpecialRequirements;

    _enrollmentRepository.Update(existingEnrollment);

    await _enrollmentRepository.SaveAsync();
}

public async Task CancelAsync(int id)
{
    var enrollment =
        await _enrollmentRepository.GetByIdAsync(id);

    if (enrollment == null)
        throw new Exception("Enrollment not found.");

    // User can unenroll only from Enrolled status
    if (enrollment.Status != "Enrolled")
        throw new Exception(
            "You can unenroll only when enrollment status is Enrolled.");

    // Course must still exist
    var course = await _courseRepository
        .GetByIdAsync(enrollment.CourseId);

    if (course == null || course.IsDeleted)
        throw new Exception("Course not found.");

    // Cannot unenroll after course starts
    if (course.StartDate <= DateTime.UtcNow)
        throw new Exception(
            "You cannot unenroll after the course has started.");

    // Cancel = Dropped
    enrollment.Status = "Dropped";

    _enrollmentRepository.Update(enrollment);

    await _enrollmentRepository.SaveAsync();
}

private void ValidateStatusTransition(
    string currentStatus,
    string newStatus)
{
    if (currentStatus == "Enrolled")
    {
        if (newStatus != "Active" &&
            newStatus != "Dropped")
        {
            throw new Exception(
                "Invalid status transition.");
        }

        return;
    }

    if (currentStatus == "Active")
    {
        if (newStatus != "Completed" &&
            newStatus != "Dropped")
        {
            throw new Exception(
                "Invalid status transition.");
        }

        return;
    }

    // Completed and Dropped are final states
    if (currentStatus == "Completed" ||
        currentStatus == "Dropped")
    {
        throw new Exception(
            "Completed or Dropped enrollment cannot be changed.");
    }
}

private async Task<int> GetCurrentStudentCountAsync(
    int courseId)
{
    var enrollments =
        await _enrollmentRepository.GetAllAsync();

    return enrollments.Count(x =>
        x.CourseId == courseId &&
        (x.Status == "Enrolled" ||
         x.Status == "Active"));
}
Enter fullscreen mode Exit fullscreen mode

}

public class LessonProgressService : ILessonProgressService
{
private readonly ILessonProgressRepository _progressRepository;
private readonly IEnrollmentRepository _enrollmentRepository;
private readonly ILessonRepository _lessonRepository;

public LessonProgressService(
    ILessonProgressRepository progressRepository,
    IEnrollmentRepository enrollmentRepository,
    ILessonRepository lessonRepository)
{
    _progressRepository = progressRepository;
    _enrollmentRepository = enrollmentRepository;
    _lessonRepository = lessonRepository;
}

public async Task<LessonProgress?> GetByIdAsync(int id)
{
    if (id <= 0)
        throw new ArgumentException("Invalid progress id.");

    return await _progressRepository.GetByIdAsync(id);
}

public async Task<List<LessonProgress>> GetByEnrollmentIdAsync(
    int enrollmentId)
{
    if (enrollmentId <= 0)
        throw new ArgumentException("Invalid enrollment id.");

    var enrollment =
        await _enrollmentRepository.GetByIdAsync(enrollmentId);

    if (enrollment == null)
        throw new Exception("Enrollment not found.");

    return await _progressRepository
        .GetByEnrollmentIdAsync(enrollmentId);
}

public async Task AddAsync(LessonProgress progress)
{
    // Enrollment must exist
    var enrollment =
        await _enrollmentRepository
            .GetByIdAsync(progress.EnrollmentId);

    if (enrollment == null)
        throw new Exception("Enrollment not found.");

    // Lesson must exist
    var lesson =
        await _lessonRepository
            .GetByIdAsync(progress.LessonId);

    if (lesson == null)
        throw new Exception("Lesson not found.");

    // Lesson must belong to enrolled course
    if (lesson.CourseId != enrollment.CourseId)
        throw new Exception(
            "Lesson does not belong to the enrolled course.");

    // User must match enrollment user
    if (progress.UserId != enrollment.UserId)
        throw new Exception(
            "User does not belong to this enrollment.");

    // Prevent duplicate progress for same lesson
    var existingProgress =
        await _progressRepository
            .GetByEnrollmentIdAsync(progress.EnrollmentId);

    bool alreadyExists = existingProgress.Any(x =>
        x.LessonId == progress.LessonId);

    if (alreadyExists)
        throw new Exception(
            "Progress for this lesson already exists.");

    // Completion date
    if (progress.IsCompleted)
    {
        progress.CompletedDate = DateTime.UtcNow;
    }
    else
    {
        progress.CompletedDate = null;
    }

    await _progressRepository.AddAsync(progress);

    await _progressRepository.SaveAsync();
}

public async Task UpdateAsync(LessonProgress progress)
{
    var existingProgress =
        await _progressRepository
            .GetByIdAsync(progress.Id);

    if (existingProgress == null)
        throw new Exception("Lesson progress not found.");

    // Enrollment must exist
    var enrollment =
        await _enrollmentRepository
            .GetByIdAsync(existingProgress.EnrollmentId);

    if (enrollment == null)
        throw new Exception("Enrollment not found.");

    // User must match enrollment
    if (progress.UserId != enrollment.UserId)
        throw new Exception(
            "User does not belong to this enrollment.");

    // Lesson must belong to enrollment course
    var lesson =
        await _lessonRepository
            .GetByIdAsync(existingProgress.LessonId);

    if (lesson == null)
        throw new Exception("Lesson not found.");

    if (lesson.CourseId != enrollment.CourseId)
        throw new Exception(
            "Lesson does not belong to the enrolled course.");

    // Update completion status
    existingProgress.IsCompleted =
        progress.IsCompleted;

    if (progress.IsCompleted)
    {
        existingProgress.CompletedDate =
            existingProgress.CompletedDate
            ?? DateTime.UtcNow;
    }
    else
    {
        existingProgress.CompletedDate = null;
    }

    _progressRepository.Update(existingProgress);

    await _progressRepository.SaveAsync();
}
Enter fullscreen mode Exit fullscreen mode

}

public class ReviewService : IReviewService
{
private readonly IReviewRepository _reviewRepository;
private readonly ICourseRepository _courseRepository;
private readonly IEnrollmentRepository _enrollmentRepository;

public ReviewService(
    IReviewRepository reviewRepository,
    ICourseRepository courseRepository,
    IEnrollmentRepository enrollmentRepository)
{
    _reviewRepository = reviewRepository;
    _courseRepository = courseRepository;
    _enrollmentRepository = enrollmentRepository;
}

public async Task<List<Review>> GetByCourseIdAsync(int courseId)
{
    if (courseId <= 0)
        throw new ArgumentException("Invalid course id.");

    var course = await _courseRepository.GetByIdAsync(courseId);

    if (course == null || course.IsDeleted)
        throw new Exception("Course not found.");

    return await _reviewRepository
        .GetByCourseIdAsync(courseId);
}

public async Task<Review?> GetByIdAsync(int id)
{
    if (id <= 0)
        throw new ArgumentException("Invalid review id.");

    return await _reviewRepository.GetByIdAsync(id);
}

public async Task AddAsync(Review review)
{
    // Course must exist
    var course = await _courseRepository
        .GetByIdAsync(review.CourseId);

    if (course == null || course.IsDeleted)
        throw new Exception("Course not found.");

    // User must have an enrollment in this course
    var userEnrollments =
        await _enrollmentRepository
            .GetByUserIdAsync(review.UserId);

    var enrollment = userEnrollments.FirstOrDefault(e =>
        e.CourseId == review.CourseId &&
        e.Status != "Dropped");

    if (enrollment == null)
        throw new Exception(
            "Only enrolled students can review this course.");

    // User can review a course only once
    var existingReviews =
        await _reviewRepository
            .GetByCourseIdAsync(review.CourseId);

    bool alreadyReviewed = existingReviews.Any(r =>
        r.UserId == review.UserId);

    if (alreadyReviewed)
        throw new Exception(
            "You have already reviewed this course.");

    review.CreatedDate = DateTime.UtcNow;
    review.UpdatedDate = null;

    await _reviewRepository.AddAsync(review);

    await _reviewRepository.SaveAsync();
}

public async Task UpdateAsync(Review review)
{
    var existingReview =
        await _reviewRepository.GetByIdAsync(review.Id);

    if (existingReview == null)
        throw new Exception("Review not found.");

    // Only review owner can update
    if (existingReview.UserId != review.UserId)
        throw new Exception(
            "You can update only your own review.");

    // Course must still exist
    var course = await _courseRepository
        .GetByIdAsync(existingReview.CourseId);

    if (course == null || course.IsDeleted)
        throw new Exception("Course not found.");

    existingReview.Rating = review.Rating;
    existingReview.Comment = review.Comment;
    existingReview.UpdatedDate = DateTime.UtcNow;

    _reviewRepository.Update(existingReview);

    await _reviewRepository.SaveAsync();
}

public async Task DeleteAsync(int id)
{
    var review =
        await _reviewRepository.GetByIdAsync(id);

    if (review == null)
        throw new Exception("Review not found.");

    _reviewRepository.Delete(review);

    await _reviewRepository.SaveAsync();
}
Enter fullscreen mode Exit fullscreen mode

}

};

Top comments (0)