using FluentValidation.TestHelper;
using SatelliteProvider.Api.DTOs;
using SatelliteProvider.Api.Validators;

namespace SatelliteProvider.Tests.Validators;

// AZ-811: unit tests for GetTileByLatLonQueryValidator. One Theory per RuleFor
// covering boundary + out-of-range. Unknown-query-param rejection is tested
// at the integration layer (GetTileByLatLonValidationTests) — there's no
// pure-unit equivalent because the filter runs against HttpContext.Request.Query.
public class GetTileByLatLonQueryValidatorTests
{
    private readonly GetTileByLatLonQueryValidator _validator;

    public GetTileByLatLonQueryValidatorTests()
    {
        GlobalValidatorConfig.ApplyOnce();
        _validator = new GetTileByLatLonQueryValidator();
    }

    [Theory]
    [InlineData(-90.001)]
    [InlineData(90.001)]
    [InlineData(180.0)]
    public void Validate_LatOutOfRange_FailsRangeRule(double lat)
    {
        // Arrange
        var query = new GetTileByLatLonQuery(lat, 37.647063, 18);

        // Act
        var result = _validator.TestValidate(query);

        // Assert
        result.ShouldHaveValidationErrorFor("lat");
    }

    [Fact]
    public void Validate_LatNull_FailsNotNullRule()
    {
        // Arrange
        var query = new GetTileByLatLonQuery(null, 37.647063, 18);

        // Act
        var result = _validator.TestValidate(query);

        // Assert — CascadeMode.Stop ensures NotNull short-circuits the range
        // rule, so the caller sees only `"\`lat\` is required."` not also the
        // range error against a null sentinel.
        result.ShouldHaveValidationErrorFor("lat").WithErrorMessage("`lat` is required.");
    }

    [Theory]
    [InlineData(-90.0)]
    [InlineData(0.0)]
    [InlineData(47.461747)]
    [InlineData(90.0)]
    public void Validate_LatAtOrInsideBounds_Passes(double lat)
    {
        // Arrange
        var query = new GetTileByLatLonQuery(lat, 37.647063, 18);

        // Act
        var result = _validator.TestValidate(query);

        // Assert
        result.ShouldNotHaveValidationErrorFor("lat");
    }

    [Theory]
    [InlineData(-180.001)]
    [InlineData(180.001)]
    [InlineData(360.0)]
    public void Validate_LonOutOfRange_FailsRangeRule(double lon)
    {
        // Arrange
        var query = new GetTileByLatLonQuery(47.461747, lon, 18);

        // Act
        var result = _validator.TestValidate(query);

        // Assert
        result.ShouldHaveValidationErrorFor("lon");
    }

    [Fact]
    public void Validate_LonNull_FailsNotNullRule()
    {
        // Arrange
        var query = new GetTileByLatLonQuery(47.461747, null, 18);

        // Act
        var result = _validator.TestValidate(query);

        // Assert
        result.ShouldHaveValidationErrorFor("lon").WithErrorMessage("`lon` is required.");
    }

    [Theory]
    [InlineData(-180.0)]
    [InlineData(0.0)]
    [InlineData(37.647063)]
    [InlineData(180.0)]
    public void Validate_LonAtOrInsideBounds_Passes(double lon)
    {
        // Arrange
        var query = new GetTileByLatLonQuery(47.461747, lon, 18);

        // Act
        var result = _validator.TestValidate(query);

        // Assert
        result.ShouldNotHaveValidationErrorFor("lon");
    }

    [Theory]
    [InlineData(-1)]
    [InlineData(23)]
    [InlineData(100)]
    public void Validate_ZoomOutOfRange_FailsRangeRule(int zoom)
    {
        // Arrange
        var query = new GetTileByLatLonQuery(47.461747, 37.647063, zoom);

        // Act
        var result = _validator.TestValidate(query);

        // Assert
        result.ShouldHaveValidationErrorFor("zoom");
    }

    [Fact]
    public void Validate_ZoomNull_FailsNotNullRule()
    {
        // Arrange
        var query = new GetTileByLatLonQuery(47.461747, 37.647063, null);

        // Act
        var result = _validator.TestValidate(query);

        // Assert
        result.ShouldHaveValidationErrorFor("zoom").WithErrorMessage("`zoom` is required.");
    }

    [Theory]
    [InlineData(0)]
    [InlineData(18)]
    [InlineData(22)]
    public void Validate_ZoomAtOrInsideBounds_Passes(int zoom)
    {
        // Arrange
        var query = new GetTileByLatLonQuery(47.461747, 37.647063, zoom);

        // Act
        var result = _validator.TestValidate(query);

        // Assert
        result.ShouldNotHaveValidationErrorFor("zoom");
    }
}
