using FluentValidation.TestHelper;
using SatelliteProvider.Api.Validators;
using SatelliteProvider.Common.DTO;

namespace SatelliteProvider.Tests.Validators;

// AZ-809: unit tests for GeofencePolygonValidator. Covers (a) presence of
// both corners, (b) range checks per corner, and (c) the cross-field
// invariant `NW north-of SE` AND `NW west-of SE`.
public class GeofencePolygonValidatorTests
{
    private readonly GeofencePolygonValidator _validator;

    public GeofencePolygonValidatorTests()
    {
        GlobalValidatorConfig.ApplyOnce();
        _validator = new GeofencePolygonValidator();
    }

    private static GeofencePolygon ValidPolygon() => new()
    {
        NorthWest = new GeoPoint(50.15, 36.05),
        SouthEast = new GeoPoint(50.05, 36.15),
    };

    [Fact]
    public void Validate_AllValid_Passes()
    {
        // Arrange
        var polygon = ValidPolygon();

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

        // Assert
        result.ShouldNotHaveAnyValidationErrors();
    }

    [Fact]
    public void Validate_NorthWestNull_FailsNotNullRule()
    {
        // Arrange
        var polygon = ValidPolygon();
        polygon.NorthWest = null;

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

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

    [Fact]
    public void Validate_SouthEastNull_FailsNotNullRule()
    {
        // Arrange
        var polygon = ValidPolygon();
        polygon.SouthEast = null;

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

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

    [Theory]
    [InlineData(-90.001)]
    [InlineData(90.001)]
    public void Validate_NorthWestLatOutOfRange_FailsRangeRule(double lat)
    {
        // Arrange
        var polygon = ValidPolygon();
        polygon.NorthWest = new GeoPoint(lat, 36.05);

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

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

    [Theory]
    [InlineData(-180.001)]
    [InlineData(180.001)]
    public void Validate_SouthEastLonOutOfRange_FailsRangeRule(double lon)
    {
        // Arrange
        var polygon = ValidPolygon();
        polygon.SouthEast = new GeoPoint(50.05, lon);

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

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

    [Fact]
    public void Validate_NorthWestLatNotGreaterThanSouthEast_FailsInvariant()
    {
        // Arrange — NW.Lat <= SE.Lat → invariant violation
        var polygon = ValidPolygon();
        polygon.NorthWest = new GeoPoint(50.05, 36.05);
        polygon.SouthEast = new GeoPoint(50.05, 36.15);

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

        // Assert
        result.ShouldHaveValidationErrorFor("northWest")
            .WithErrorMessage("`northWest.lat` must be greater than `southEast.lat` (NW is north-of SE).");
    }

    [Fact]
    public void Validate_NorthWestLonNotLessThanSouthEast_FailsInvariant()
    {
        // Arrange — NW.Lon >= SE.Lon → invariant violation
        var polygon = ValidPolygon();
        polygon.NorthWest = new GeoPoint(50.15, 36.15);
        polygon.SouthEast = new GeoPoint(50.05, 36.15);

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

        // Assert
        result.ShouldHaveValidationErrorFor("northWest")
            .WithErrorMessage("`northWest.lon` must be less than `southEast.lon` (NW is west-of SE).");
    }
}
