namespace Domain.Tests.Entities;

using Domain.Entities;
using Domain.ValueObjects;
using FluentAssertions;
using Xunit;

public class StudentTests
{
    private static Email ValidEmail => Email.Create("test@example.com");

    [Fact]
    public void Constructor_WithValidData_ShouldCreateStudent()
    {
        var student = new Student("John Doe", ValidEmail);

        student.Name.Should().Be("John Doe");
        student.Email.Value.Should().Be("test@example.com");
        student.Enrollments.Should().BeEmpty();
        student.TotalCredits.Should().Be(0);
    }

    [Theory]
    [InlineData("")]
    [InlineData("   ")]
    [InlineData(null)]
    public void Constructor_WithInvalidName_ShouldThrow(string? name)
    {
        var act = () => new Student(name!, ValidEmail);

        act.Should().Throw<ArgumentException>()
            .WithMessage("*Student name is required*");
    }

    [Fact]
    public void Constructor_WithNullEmail_ShouldThrow()
    {
        var act = () => new Student("John Doe", null!);

        act.Should().Throw<ArgumentNullException>();
    }

    [Fact]
    public void CanEnroll_WhenNoEnrollments_ShouldReturnTrue()
    {
        var student = new Student("John Doe", ValidEmail);

        student.CanEnroll().Should().BeTrue();
    }

    [Fact]
    public void CanEnroll_WhenMaxEnrollments_ShouldReturnFalse()
    {
        var student = new Student("John Doe", ValidEmail);
        AddEnrollments(student, 3);

        student.CanEnroll().Should().BeFalse();
    }

    [Fact]
    public void HasProfessor_WhenStudentHasSubjectWithProfessor_ShouldReturnTrue()
    {
        var student = new Student("John Doe", ValidEmail);
        var subject = new Subject("Math", professorId: 1);
        var enrollment = new Enrollment(student.Id, subject.Id);
        SetSubjectOnEnrollment(enrollment, subject);
        student.AddEnrollment(enrollment);

        student.HasProfessor(1).Should().BeTrue();
    }

    [Fact]
    public void HasProfessor_WhenNoProfessor_ShouldReturnFalse()
    {
        var student = new Student("John Doe", ValidEmail);

        student.HasProfessor(1).Should().BeFalse();
    }

    [Fact]
    public void UpdateName_WithValidName_ShouldUpdate()
    {
        var student = new Student("John Doe", ValidEmail);

        student.UpdateName("Jane Doe");

        student.Name.Should().Be("Jane Doe");
    }

    [Fact]
    public void UpdateEmail_WithValidEmail_ShouldUpdate()
    {
        var student = new Student("John Doe", ValidEmail);
        var newEmail = Email.Create("new@example.com");

        student.UpdateEmail(newEmail);

        student.Email.Value.Should().Be("new@example.com");
    }

    [Fact]
    public void TotalCredits_ShouldSumEnrollmentCredits()
    {
        var student = new Student("John Doe", ValidEmail);
        AddEnrollments(student, 2);

        student.TotalCredits.Should().Be(6);
    }

    private static void AddEnrollments(Student student, int count)
    {
        for (int i = 0; i < count; i++)
        {
            var subject = new Subject($"Subject{i}", professorId: i + 1);
            var enrollment = new Enrollment(student.Id, subject.Id);
            SetSubjectOnEnrollment(enrollment, subject);
            student.AddEnrollment(enrollment);
        }
    }

    private static void SetSubjectOnEnrollment(Enrollment enrollment, Subject subject)
    {
        var subjectProperty = typeof(Enrollment).GetProperty("Subject")!;
        subjectProperty.SetValue(enrollment, subject);
    }
}
