namespace Application.Tests.Professors;

using Application.Professors.DTOs;
using Application.Professors.Queries;
using Domain.Ports.Repositories;
using FluentAssertions;
using NSubstitute;
using System.Linq.Expressions;
using Xunit;

public class GetProfessorsQueryTests
{
    private readonly IProfessorRepository _professorRepository;
    private readonly GetProfessorsHandler _handler;

    public GetProfessorsQueryTests()
    {
        _professorRepository = Substitute.For<IProfessorRepository>();
        _handler = new GetProfessorsHandler(_professorRepository);
    }

    [Fact]
    public async Task Handle_ShouldReturnAllProfessors()
    {
        // Arrange
        var professors = new List<ProfessorDto>
        {
            new(1, "Prof A", [new ProfessorSubjectDto(1, "Math", 3), new ProfessorSubjectDto(2, "Physics", 3)]),
            new(2, "Prof B", [new ProfessorSubjectDto(3, "Chemistry", 3)])
        };

        _professorRepository.GetAllProjectedAsync(
            Arg.Any<Expression<Func<Domain.Entities.Professor, ProfessorDto>>>(),
            Arg.Any<CancellationToken>())
            .Returns(professors);

        var query = new GetProfessorsQuery();

        // Act
        var result = await _handler.Handle(query, CancellationToken.None);

        // Assert
        result.Should().HaveCount(2);
        result[0].Name.Should().Be("Prof A");
        result[0].Subjects.Should().HaveCount(2);
    }

    [Fact]
    public async Task Handle_WhenNoProfessors_ShouldReturnEmptyList()
    {
        // Arrange
        _professorRepository.GetAllProjectedAsync(
            Arg.Any<Expression<Func<Domain.Entities.Professor, ProfessorDto>>>(),
            Arg.Any<CancellationToken>())
            .Returns(new List<ProfessorDto>());

        var query = new GetProfessorsQuery();

        // Act
        var result = await _handler.Handle(query, CancellationToken.None);

        // Assert
        result.Should().BeEmpty();
    }

    [Fact]
    public async Task Handle_ShouldReturnProfessorsWithSubjects()
    {
        // Arrange
        var subjects = new List<ProfessorSubjectDto>
        {
            new(1, "Math", 3),
            new(2, "Physics", 3)
        };
        var professors = new List<ProfessorDto>
        {
            new(1, "Prof A", subjects)
        };

        _professorRepository.GetAllProjectedAsync(
            Arg.Any<Expression<Func<Domain.Entities.Professor, ProfessorDto>>>(),
            Arg.Any<CancellationToken>())
            .Returns(professors);

        var query = new GetProfessorsQuery();

        // Act
        var result = await _handler.Handle(query, CancellationToken.None);

        // Assert
        result[0].Subjects.Should().Contain(s => s.Name == "Math");
        result[0].Subjects.Should().Contain(s => s.Name == "Physics");
    }
}
