using FluentAssertions;
using Microsoft.Extensions.Options;
using SatelliteProvider.Common.Configs;
using SatelliteProvider.Common.DTO;
using SatelliteProvider.Services.RouteManagement;
using SatelliteProvider.Tests.Fixtures;

namespace SatelliteProvider.Tests;

public class RouteValidatorTests
{
    private static RouteValidator MakeValidator() =>
        new(Options.Create(new ProcessingConfig()));

    private static CreateRouteRequest BuildValidRequest()
    {
        return new CreateRouteRequest
        {
            Id = Guid.NewGuid(),
            Name = "valid-route",
            Description = "test",
            RegionSizeMeters = 500,
            ZoomLevel = 18,
            Points = TestCoordinates.Route.Route01Points
                .Select(p => new RoutePoint { Latitude = p.Lat, Longitude = p.Lon })
                .ToList(),
        };
    }

    [Fact]
    public void Validate_ValidRequest_DoesNotThrow_AZ365_AC2()
    {
        var sut = MakeValidator();
        var request = BuildValidRequest();

        Action act = () => sut.Validate(request);

        act.Should().NotThrow();
    }

    [Fact]
    public void Validate_FewerThanTwoPoints_Throws()
    {
        var sut = MakeValidator();
        var request = BuildValidRequest();
        request.Points = new List<RoutePoint> { new() { Latitude = 47.46, Longitude = 37.64 } };

        Action act = () => sut.Validate(request);

        act.Should().Throw<ArgumentException>().WithMessage("*at least 2 points*");
    }

    [Fact]
    public void Validate_RegionSizeOutOfRange_Throws()
    {
        var sut = MakeValidator();
        var request = BuildValidRequest();
        request.RegionSizeMeters = 50;

        Action act = () => sut.Validate(request);

        act.Should().Throw<ArgumentException>()
            .WithMessage("*Region size must be between 100 and 10000*");
    }

    [Fact]
    public void Validate_BlankName_Throws()
    {
        var sut = MakeValidator();
        var request = BuildValidRequest();
        request.Name = "   ";

        Action act = () => sut.Validate(request);

        act.Should().Throw<ArgumentException>().WithMessage("*Route name is required*");
    }

    [Fact]
    public void Validate_GeofencePolygonZeroZero_Throws()
    {
        var sut = MakeValidator();
        var request = BuildValidRequest();
        request.Geofences = new Geofences
        {
            Polygons = new List<GeofencePolygon>
            {
                new() { NorthWest = new GeoPoint(0, 0), SouthEast = new GeoPoint(0, 0) },
            },
        };

        Action act = () => sut.Validate(request);

        act.Should().Throw<ArgumentException>()
            .WithMessage("*coordinates cannot be (0,0)*");
    }

    [Fact]
    public void Validate_GeofenceInvertedLatitudes_Throws()
    {
        var sut = MakeValidator();
        var request = BuildValidRequest();
        request.Geofences = new Geofences
        {
            Polygons = new List<GeofencePolygon>
            {
                new()
                {
                    NorthWest = new GeoPoint(48.250, 37.370),
                    SouthEast = new GeoPoint(48.280, 37.395),
                },
            },
        };

        Action act = () => sut.Validate(request);

        act.Should().Throw<ArgumentException>().WithMessage("*northWest latitude*");
    }

    [Fact]
    public void Validate_NullPolygonCorner_Throws()
    {
        var sut = MakeValidator();
        var request = BuildValidRequest();
        request.Geofences = new Geofences
        {
            Polygons = new List<GeofencePolygon>
            {
                new() { NorthWest = null, SouthEast = new GeoPoint(48.260, 37.390) },
            },
        };

        Action act = () => sut.Validate(request);

        act.Should().Throw<ArgumentException>()
            .WithMessage("*polygon coordinates are required*");
    }

    [Fact]
    public void Validate_OutOfRangeLatitude_Throws()
    {
        var sut = MakeValidator();
        var request = BuildValidRequest();
        request.Geofences = new Geofences
        {
            Polygons = new List<GeofencePolygon>
            {
                new()
                {
                    NorthWest = new GeoPoint(95, 37.370),
                    SouthEast = new GeoPoint(48.265, 37.395),
                },
            },
        };

        Action act = () => sut.Validate(request);

        act.Should().Throw<ArgumentException>()
            .WithMessage("*coordinates must be valid*");
    }

    [Fact]
    public void Validate_MultipleErrors_AggregatesIntoSingleException_AZ365_AC2()
    {
        var sut = MakeValidator();
        var request = BuildValidRequest();
        request.Name = "";
        request.RegionSizeMeters = 50;
        request.Points = new List<RoutePoint>();

        Action act = () => sut.Validate(request);

        act.Should().Throw<ArgumentException>()
            .Where(ex =>
                ex.Message.Contains("at least 2 points")
                && ex.Message.Contains("Region size must be between 100 and 10000")
                && ex.Message.Contains("Route name is required"),
                "AZ-365 AC-2: validator aggregates all failures into a single ArgumentException");
    }

    [Fact]
    public void Validate_NullRequest_Throws()
    {
        var sut = MakeValidator();

        Action act = () => sut.Validate(null!);

        act.Should().Throw<ArgumentNullException>();
    }
}
