using FluentValidation;

public class LoginRequestValidator : AbstractValidator<LoginRequest>
{
    public LoginRequestValidator()
    {
        RuleFor(x => x.Username).NotEmpty();
        RuleFor(x => x.Password).NotEmpty();
    }
}

public class RefreshRequestValidator : AbstractValidator<RefreshRequest>
{
    public RefreshRequestValidator()
    {
        RuleFor(x => x.RefreshToken).NotEmpty();
    }
}

public class LogoutRequestValidator : AbstractValidator<LogoutRequest>
{
    public LogoutRequestValidator()
    {
        RuleFor(x => x.RefreshToken).NotEmpty();
    }
}

public class UpsertDraftPatientRequestValidator : AbstractValidator<UpsertDraftPatientRequest>
{
    public UpsertDraftPatientRequestValidator()
    {
        RuleFor(x => x.FullName).MaximumLength(200).When(x => x.FullName is not null);
        RuleFor(x => x.DateOfBirth).LessThanOrEqualTo(DateOnly.FromDateTime(DateTime.UtcNow))
            .WithMessage("Date of birth cannot be in the future.")
            .When(x => x.DateOfBirth.HasValue);
        RuleFor(x => x.Sex)
            .Must(s => IsAllowedSex(s))
            .WithMessage("Sex must be 'male', 'female', 'other', or 'unknown'.")
            .When(x => x.Sex is not null);
    }

    private static bool IsAllowedSex(string? sex)
    {
        if (sex is null) return true;

        return sex.Trim().ToLowerInvariant() switch
        {
            "m" or "male" or "f" or "female" or "other" or "unknown" => true,
            _ => false
        };
    }
}

public class UpsertDraftEncounterRequestValidator : AbstractValidator<UpsertDraftEncounterRequest>
{
    public UpsertDraftEncounterRequestValidator()
    {
        RuleFor(x => x.AdmissionDate).LessThanOrEqualTo(DateTimeOffset.UtcNow)
            .WithMessage("Admission date cannot be in the future.")
            .When(x => x.AdmissionDate.HasValue);
        RuleFor(x => x.RoomBed).MaximumLength(50).When(x => x.RoomBed is not null);
        RuleFor(x => x.AdmissionReason).MaximumLength(500).When(x => x.AdmissionReason is not null);
        RuleFor(x => x.DischargeDiagnosis).MaximumLength(500).When(x => x.DischargeDiagnosis is not null);
    }
}

public class CreateDraftObservationRequestValidator : AbstractValidator<CreateDraftObservationRequest>
{
    public CreateDraftObservationRequestValidator()
    {
        RuleFor(x => x.ObservationCode).NotEmpty();
        RuleFor(x => x.Unit).NotEmpty();
        RuleFor(x => x.RecordedAt).NotEmpty()
            .LessThanOrEqualTo(DateTimeOffset.UtcNow.AddMinutes(5))
            .WithMessage("RecordedAt cannot be in the future.");
    }
}

public class UpdateDraftObservationRequestValidator : AbstractValidator<UpdateDraftObservationRequest>
{
    public UpdateDraftObservationRequestValidator()
    {
        RuleFor(x => x.ObservationCode).NotEmpty();
        RuleFor(x => x.Unit).NotEmpty();
        RuleFor(x => x.RecordedAt).NotEmpty()
            .LessThanOrEqualTo(DateTimeOffset.UtcNow.AddMinutes(5))
            .WithMessage("RecordedAt cannot be in the future.");
    }
}

public class VerifyBatchRequestValidator : AbstractValidator<VerifyBatchRequest>
{
    private static readonly HashSet<string> _validStatuses = new() { "ok", "warning", "error" };

    public VerifyBatchRequestValidator()
    {
        RuleFor(x => x.FieldChecks).NotEmpty()
            .WithMessage("At least one field check is required.");
        RuleForEach(x => x.FieldChecks).ChildRules(check =>
        {
            check.RuleFor(c => c.FieldName).NotEmpty();
            check.RuleFor(c => c.Status).NotEmpty()
                .Must(s => _validStatuses.Contains(s))
                .WithMessage("Status must be 'ok', 'warning', or 'error'.");
        });
    }
}

public class RejectBatchRequestValidator : AbstractValidator<RejectBatchRequest>
{
    public RejectBatchRequestValidator()
    {
        RuleFor(x => x.Reason)
            .NotEmpty()
            .WithErrorCode("REJECTION_REASON_REQUIRED")
            .WithMessage("Rejection reason is required.");

        RuleFor(x => x.Reason)
            .MinimumLength(10)
            .When(x => !string.IsNullOrEmpty(x.Reason))
            .WithErrorCode("REJECTION_REASON_TOO_SHORT")
            .WithMessage("Rejection reason must be at least 10 characters.");
    }
}

public class RecordObservationsRequestValidator : AbstractValidator<RecordObservationsRequest>
{
    public RecordObservationsRequestValidator()
    {
        RuleFor(x => x.Observations)
            .NotEmpty()
            .WithErrorCode("EMPTY_OBSERVATIONS")
            .WithMessage("At least one observation is required.")
            .Must(o => o.Count <= 10)
            .WithMessage("Maximum 10 observations per request.");
        RuleFor(x => x.ClinicianAttestation)
            .Equal(true)
            .WithErrorCode("ATTESTATION_REQUIRED")
            .WithMessage("Clinician attestation is required.");
        RuleFor(x => x.PasswordConfirm).NotEmpty();
        RuleForEach(x => x.Observations).SetValidator(new LiveCaptureObservationRequestValidator());
    }
}

public class OpenEncounterWithVitalsRequestValidator : AbstractValidator<OpenEncounterWithVitalsRequest>
{
    public OpenEncounterWithVitalsRequestValidator()
    {
        RuleFor(x => x.PatientId).NotEmpty();
        RuleFor(x => x.Department).NotEmpty();
        RuleFor(x => x.AdmissionReason).NotEmpty();
        RuleFor(x => x.Observations)
            .NotEmpty()
            .WithErrorCode("EMPTY_OBSERVATIONS")
            .WithMessage("At least one observation is required.")
            .Must(o => o.Count <= 10)
            .WithMessage("Maximum 10 observations per request.");
        RuleFor(x => x.ClinicianAttestation)
            .Equal(true)
            .WithErrorCode("ATTESTATION_REQUIRED")
            .WithMessage("Clinician attestation is required.");
        RuleFor(x => x.PasswordConfirm).NotEmpty();
        RuleForEach(x => x.Observations).SetValidator(new LiveCaptureObservationRequestValidator());
    }
}

public class LiveCaptureObservationRequestValidator : AbstractValidator<LiveCaptureObservationRequest>
{
    public LiveCaptureObservationRequestValidator()
    {
        RuleFor(x => x.ObservationCode).NotEmpty();
        RuleFor(x => x.Unit).NotEmpty();
        RuleFor(x => x.RecordedAt).NotEmpty();
    }
}

public class CreateUserRequestValidator : AbstractValidator<CreateUserRequest>
{
    private static readonly HashSet<string> _validRoles = new()
    {
        "INTAKE_CLERK", "DATA_ENTRY_CLERK", "VERIFIER",
        "CLINICAL_APPROVER", "CLINICIAN", "ADMINISTRATOR"
    };

    public CreateUserRequestValidator()
    {
        RuleFor(x => x.Username).NotEmpty().MaximumLength(50);
        RuleFor(x => x.Password).NotEmpty().MinimumLength(8)
            .Matches("[A-Z]").WithMessage("Password must contain at least one uppercase letter.")
            .Matches("[0-9]").WithMessage("Password must contain at least one digit.");
        RuleFor(x => x.FullName).NotEmpty().MaximumLength(200);
        RuleFor(x => x.Role).NotEmpty()
            .Must(r => _validRoles.Contains(r))
            .WithMessage("Role must be one of: INTAKE_CLERK, DATA_ENTRY_CLERK, VERIFIER, CLINICAL_APPROVER, CLINICIAN, ADMINISTRATOR.");
    }
}

public class UpdateUserRequestValidator : AbstractValidator<UpdateUserRequest>
{
    private static readonly HashSet<string> _validRoles = new()
    {
        "INTAKE_CLERK", "DATA_ENTRY_CLERK", "VERIFIER",
        "CLINICAL_APPROVER", "CLINICIAN", "ADMINISTRATOR"
    };

    public UpdateUserRequestValidator()
    {
        RuleFor(x => x.FullName).MaximumLength(200).When(x => x.FullName is not null);
        RuleFor(x => x.Role)
            .Must(r => _validRoles.Contains(r!))
            .WithMessage("Role must be one of: INTAKE_CLERK, DATA_ENTRY_CLERK, VERIFIER, CLINICAL_APPROVER, CLINICIAN, ADMINISTRATOR.")
            .When(x => x.Role is not null);
    }
}

public class ResetPasswordRequestValidator : AbstractValidator<ResetPasswordRequest>
{
    public ResetPasswordRequestValidator()
    {
        RuleFor(x => x.NewPassword).NotEmpty().MinimumLength(8)
            .Matches("[A-Z]").WithMessage("Password must contain at least one uppercase letter.")
            .Matches("[0-9]").WithMessage("Password must contain at least one digit.");
    }
}

public class ChangePasswordRequestValidator : AbstractValidator<ChangePasswordRequest>
{
    public ChangePasswordRequestValidator()
    {
        RuleFor(x => x.CurrentPassword).NotEmpty();
        RuleFor(x => x.NewPassword).NotEmpty().MinimumLength(8)
            .Matches("[A-Z]").WithMessage("Password must contain at least one uppercase letter.")
            .Matches("[0-9]").WithMessage("Password must contain at least one digit.");
    }
}

public class CancelBatchRequestValidator : AbstractValidator<CancelBatchRequest>
{
    public CancelBatchRequestValidator()
    {
        RuleFor(x => x.Reason).NotEmpty().MinimumLength(5)
            .WithMessage("Cancellation reason must be at least 5 characters.");
    }
}
