using FluentValidation;
using SatelliteProvider.Api.DTOs;

namespace SatelliteProvider.Api.Validators;

// AZ-811: FluentValidation rules for the query-string surface of
// GET /api/satellite/tiles/latlon. Wired through
// ValidationEndpointFilter<GetTileByLatLonQuery> at endpoint registration
// time (.WithValidation<GetTileByLatLonQuery>() in Program.cs).
//
// Each rule maps 1:1 to a query parameter; errors[] keys are camelCase per
// GlobalValidatorConfig (matching the wire-format param names `lat`, `lon`,
// `zoom`). Required-field detection is `NotNull()` on the nullable-bound
// DTO (see GetTileByLatLonQuery for why properties are nullable). Each rule
// uses CascadeMode.Stop so a missing param surfaces ONLY as
// "`lat` is required" — not also "`lat` must be between -90 and 90" with a
// null value. Unknown query parameters are caught upstream by
// RejectUnknownQueryParamsEndpointFilter.
public sealed class GetTileByLatLonQueryValidator : AbstractValidator<GetTileByLatLonQuery>
{
    private const double MinLat = -90.0;
    private const double MaxLat = 90.0;
    private const double MinLon = -180.0;
    private const double MaxLon = 180.0;
    private const int MinZoom = 0;
    private const int MaxZoom = 22;

    public GetTileByLatLonQueryValidator()
    {
        RuleFor(q => q.Lat)
            .Cascade(CascadeMode.Stop)
            .NotNull().WithMessage("`lat` is required.")
            .InclusiveBetween(MinLat, MaxLat).WithMessage($"`lat` must be between {MinLat} and {MaxLat}.");

        RuleFor(q => q.Lon)
            .Cascade(CascadeMode.Stop)
            .NotNull().WithMessage("`lon` is required.")
            .InclusiveBetween(MinLon, MaxLon).WithMessage($"`lon` must be between {MinLon} and {MaxLon}.");

        RuleFor(q => q.Zoom)
            .Cascade(CascadeMode.Stop)
            .NotNull().WithMessage("`zoom` is required.")
            .InclusiveBetween(MinZoom, MaxZoom).WithMessage($"`zoom` must be between {MinZoom} and {MaxZoom} (slippy-map range).");
    }
}
