namespace Application.Students.Commands;

using Application.Common;
using Domain.Ports.Repositories;
using FluentValidation;

/// <summary>
/// Validator for <see cref="CreateStudentCommand"/>.
/// Validates name format, email uniqueness, and prevents XSS attacks.
/// </summary>
public class CreateStudentValidator : AbstractValidator<CreateStudentCommand>
{
    public CreateStudentValidator(IStudentRepository studentRepository)
    {
        RuleFor(x => x.Name)
            .NotEmpty().WithMessage("Name is required")
            .MinimumLength(ValidationPatterns.MinNameLength)
                .WithMessage($"Name must be at least {ValidationPatterns.MinNameLength} characters")
            .MaximumLength(ValidationPatterns.MaxNameLength)
                .WithMessage($"Name must not exceed {ValidationPatterns.MaxNameLength} characters")
            .Matches(ValidationPatterns.SafeNamePattern())
                .WithMessage("Name contains invalid characters")
            .Must(ValidationPatterns.IsSafeContent)
                .WithMessage("Name contains prohibited content");

        RuleFor(x => x.Email)
            .NotEmpty().WithMessage("Email is required")
            .MaximumLength(ValidationPatterns.MaxEmailLength)
                .WithMessage($"Email must not exceed {ValidationPatterns.MaxEmailLength} characters")
            .EmailAddress().WithMessage("Invalid email format")
            .Must(ValidationPatterns.IsSafeContent)
                .WithMessage("Email contains prohibited content")
            .MustAsync(async (email, ct) =>
                !await studentRepository.EmailExistsAsync(email, null, ct))
            .WithMessage("Email already exists");
    }
}

/// <summary>
/// Validator for <see cref="UpdateStudentCommand"/>.
/// Validates name format, email uniqueness (excluding current student), and prevents XSS attacks.
/// </summary>
public class UpdateStudentValidator : AbstractValidator<UpdateStudentCommand>
{
    public UpdateStudentValidator(IStudentRepository studentRepository)
    {
        RuleFor(x => x.Id)
            .GreaterThan(0).WithMessage(ValidationPatterns.InvalidIdMessage);

        RuleFor(x => x.Name)
            .NotEmpty().WithMessage("Name is required")
            .MinimumLength(ValidationPatterns.MinNameLength)
                .WithMessage($"Name must be at least {ValidationPatterns.MinNameLength} characters")
            .MaximumLength(ValidationPatterns.MaxNameLength)
                .WithMessage($"Name must not exceed {ValidationPatterns.MaxNameLength} characters")
            .Matches(ValidationPatterns.SafeNamePattern())
                .WithMessage("Name contains invalid characters")
            .Must(ValidationPatterns.IsSafeContent)
                .WithMessage("Name contains prohibited content");

        RuleFor(x => x.Email)
            .NotEmpty().WithMessage("Email is required")
            .MaximumLength(ValidationPatterns.MaxEmailLength)
                .WithMessage($"Email must not exceed {ValidationPatterns.MaxEmailLength} characters")
            .EmailAddress().WithMessage("Invalid email format")
            .Must(ValidationPatterns.IsSafeContent)
                .WithMessage("Email contains prohibited content")
            .MustAsync(async (command, email, ct) =>
                !await studentRepository.EmailExistsAsync(email, command.Id, ct))
            .WithMessage("Email already exists");
    }
}
