using FluentAssertions;
using SatelliteProvider.Common.DTO;
using SatelliteProvider.Services.RouteManagement;

namespace SatelliteProvider.Tests;

public class GeofenceGridCalculatorTests
{
    [Fact]
    public void GenerateRegions_SmallPolygon_ReturnsAtLeastOneCenter()
    {
        var sut = new GeofenceGridCalculator();
        var nw = new GeoPoint(48.280, 37.370);
        var se = new GeoPoint(48.265, 37.395);

        var regions = sut.GenerateRegions(nw, se, regionSizeMeters: 300);

        regions.Should().NotBeEmpty();
    }

    [Fact]
    public void GenerateRegions_AllCentersInsidePolygon()
    {
        var sut = new GeofenceGridCalculator();
        var nw = new GeoPoint(48.280, 37.370);
        var se = new GeoPoint(48.265, 37.395);

        var regions = sut.GenerateRegions(nw, se, regionSizeMeters: 300);

        regions.Should().OnlyContain(p =>
            p.Lat <= nw.Lat && p.Lat >= se.Lat &&
            p.Lon >= nw.Lon && p.Lon <= se.Lon);
    }

    [Fact]
    public void GenerateRegions_LargerRegionSize_ProducesFewerCenters()
    {
        var sut = new GeofenceGridCalculator();
        var nw = new GeoPoint(48.280, 37.370);
        var se = new GeoPoint(48.265, 37.395);

        var fineGrid = sut.GenerateRegions(nw, se, regionSizeMeters: 200);
        var coarseGrid = sut.GenerateRegions(nw, se, regionSizeMeters: 1000);

        coarseGrid.Count.Should().BeLessThan(fineGrid.Count);
    }

    [Fact]
    public void GenerateRegions_VeryLargeRegionSize_AlwaysReturnsAtLeastOneCenter()
    {
        var sut = new GeofenceGridCalculator();
        var nw = new GeoPoint(48.280, 37.370);
        var se = new GeoPoint(48.265, 37.395);

        var regions = sut.GenerateRegions(nw, se, regionSizeMeters: 100_000);

        regions.Should().HaveCount(1);
    }

    [Fact]
    public void GenerateRegions_NonPositiveRegionSize_Throws()
    {
        var sut = new GeofenceGridCalculator();
        var nw = new GeoPoint(48.280, 37.370);
        var se = new GeoPoint(48.265, 37.395);

        Action act = () => sut.GenerateRegions(nw, se, regionSizeMeters: 0);

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

    [Fact]
    public void GenerateRegions_CountMatchesCeilingOfDiagonalSpan()
    {
        var sut = new GeofenceGridCalculator();
        var nw = new GeoPoint(48.280, 37.370);
        var se = new GeoPoint(48.265, 37.395);

        var regions = sut.GenerateRegions(nw, se, regionSizeMeters: 300);

        var distinctLats = regions.Select(r => r.Lat).Distinct().Count();
        var distinctLons = regions.Select(r => r.Lon).Distinct().Count();
        regions.Count.Should().Be(distinctLats * distinctLons);
    }
}
