using Core.Entities; using Core.Models; using System.Reflection; namespace Core.Validation; /// /// Main service for executing validation rules and generating warnings /// public class ValidationService { private readonly ValidationConfiguration _config; private readonly List> _studentRules; private readonly List> _teamRules; private readonly List> _statisticsRules; // Lazy static singletons for rule definitions (instantiated once per application lifetime) private static readonly Lazy>> _studentRuleDefinitions = new(() => DiscoverRules()); private static readonly Lazy>> _teamRuleDefinitions = new(() => DiscoverRules()); private static readonly Lazy>> _statisticsRuleDefinitions = new(() => DiscoverRules()); /// /// Create a new validation service with the specified configuration /// /// Validation configuration (uses Default if null) public ValidationService(ValidationConfiguration? config = null) { _config = config ?? ValidationConfiguration.Default; // Use the shared rule definitions (singleton pattern) _studentRules = _studentRuleDefinitions.Value; _teamRules = _teamRuleDefinitions.Value; _statisticsRules = _statisticsRuleDefinitions.Value; } /// /// Discover all validation rules for a given entity type using reflection /// /// The entity type to find rules for /// List of discovered validation rules private static List> DiscoverRules() { var ruleType = typeof(IValidationRule); return Assembly.GetExecutingAssembly() .GetTypes() .Where(t => t.IsClass && !t.IsAbstract && ruleType.IsAssignableFrom(t)) .Select(t => (IValidationRule)Activator.CreateInstance(t)!) .ToList(); } /// /// Validate a student's event rankings /// /// Student to validate /// Validation context /// List of validation warnings public List ValidateStudentRankings(Student student, ValidationContext context) { return _studentRules .Where(rule => rule.AppliesTo(context)) .Select(rule => rule.Validate(student, _config)) .Where(warning => warning != null) .Cast() .ToList(); } /// /// Validate a team configuration /// /// Team to validate /// Validation context (defaults to Team) /// List of validation warnings public List ValidateTeam(Team team, ValidationContext context = ValidationContext.Team) { return _teamRules .Where(rule => rule.AppliesTo(context)) .Select(rule => rule.Validate(team, _config)) .Where(warning => warning != null) .Cast() .ToList(); } /// /// Validate student event assignment statistics /// /// Student statistics to validate /// Validation context /// List of validation warnings public List ValidateStudentStatistics(StudentEventStatistics stats, ValidationContext context) { return _statisticsRules .Where(rule => rule.AppliesTo(context)) .Select(rule => rule.Validate(stats, _config)) .Where(warning => warning != null) .Cast() .ToList(); } /// /// Validate all students in a collection /// /// Students to validate /// Validation context /// Dictionary mapping each student to their warnings public Dictionary> ValidateStudents( IEnumerable students, ValidationContext context) { return students.ToDictionary( student => student, student => ValidateStudentRankings(student, context) ); } /// /// Validate all teams in a collection /// /// Teams to validate /// Dictionary mapping each team to their warnings public Dictionary> ValidateTeams(IEnumerable teams) { return teams.ToDictionary( team => team, team => ValidateTeam(team) ); } }