namespace Application.Subjects.Queries;

using Application.Subjects.DTOs;
using Domain.Exceptions;
using Domain.Ports.Repositories;
using Domain.Services;
using MediatR;

/// <summary>
/// Query to get all subjects with availability status for a specific student.
/// </summary>
public record GetAvailableSubjectsQuery(int StudentId) : IRequest<IReadOnlyList<AvailableSubjectDto>>;

/// <summary>
/// Handler for <see cref="GetAvailableSubjectsQuery"/>.
/// Uses domain service for availability logic.
/// </summary>
public class GetAvailableSubjectsHandler(
    IStudentRepository studentRepository,
    ISubjectRepository subjectRepository,
    EnrollmentDomainService enrollmentDomainService)
    : IRequestHandler<GetAvailableSubjectsQuery, IReadOnlyList<AvailableSubjectDto>>
{
    public async Task<IReadOnlyList<AvailableSubjectDto>> Handle(
        GetAvailableSubjectsQuery request,
        CancellationToken ct)
    {
        var student = await studentRepository.GetByIdWithEnrollmentsAsync(request.StudentId, ct)
            ?? throw new StudentNotFoundException(request.StudentId);

        var allSubjects = await subjectRepository.GetAllWithProfessorsAsync(ct);

        return allSubjects.Select(subject =>
        {
            var (isAvailable, reason) = enrollmentDomainService.CheckSubjectAvailability(student, subject);
            return new AvailableSubjectDto(
                subject.Id,
                subject.Name,
                subject.Credits,
                subject.ProfessorId,
                subject.Professor?.Name ?? "",
                isAvailable,
                reason);
        }).ToList();
    }
}
