namespace Common.Builders;

using Domain.Entities;
using Domain.ValueObjects;

/// <summary>
/// Builder pattern for creating Student test objects.
/// Encapsulates reflection-based ID setting for cleaner tests.
/// </summary>
public class StudentBuilder
{
    private int _id = 1;
    private string _name = "John Doe";
    private string _email = "john@test.com";
    private readonly List<(Subject Subject, int EnrollmentId)> _enrollments = [];

    public StudentBuilder WithId(int id)
    {
        _id = id;
        return this;
    }

    public StudentBuilder WithName(string name)
    {
        _name = name;
        return this;
    }

    public StudentBuilder WithEmail(string email)
    {
        _email = email;
        return this;
    }

    public StudentBuilder WithEnrollment(Subject subject, int enrollmentId = 0)
    {
        _enrollments.Add((subject, enrollmentId));
        return this;
    }

    public StudentBuilder WithEnrollments(int count)
    {
        for (int i = 0; i < count; i++)
        {
            var subject = new SubjectBuilder()
                .WithId(i + 100)
                .WithName($"Subject{i}")
                .WithProfessorId(i + 1)
                .WithProfessor(new ProfessorBuilder()
                    .WithId(i + 1)
                    .WithName($"Prof{i}")
                    .Build())
                .Build();
            _enrollments.Add((subject, i + 1));
        }
        return this;
    }

    public Student Build()
    {
        var student = new Student(_name, Email.Create(_email));
        SetProperty(student, "Id", _id);

        foreach (var (subject, enrollmentId) in _enrollments)
        {
            var enrollment = new Enrollment(student.Id, subject.Id);
            if (enrollmentId > 0)
                SetProperty(enrollment, "Id", enrollmentId);
            SetProperty(enrollment, "Subject", subject);
            student.AddEnrollment(enrollment);
        }

        return student;
    }

    private static void SetProperty<T>(T obj, string propertyName, object value) where T : class =>
        typeof(T).GetProperty(propertyName)!.SetValue(obj, value);
}
