using System;
using System.Net;
using StellaOps.Auth.Abstractions;
using Xunit;

namespace StellaOps.Auth.Abstractions.Tests;

public class NetworkMaskMatcherTests
{
    [Fact]
    public void Parse_SingleAddress_YieldsHostMask()
    {
        var mask = NetworkMask.Parse("192.168.1.42");

        Assert.Equal(32, mask.PrefixLength);
        Assert.True(mask.Contains(IPAddress.Parse("192.168.1.42")));
        Assert.False(mask.Contains(IPAddress.Parse("192.168.1.43")));
    }

    [Fact]
    public void Parse_Cidr_NormalisesHostBits()
    {
        var mask = NetworkMask.Parse("10.0.15.9/20");

        Assert.Equal("10.0.0.0/20", mask.ToString());
        Assert.True(mask.Contains(IPAddress.Parse("10.0.8.1")));
        Assert.False(mask.Contains(IPAddress.Parse("10.0.32.1")));
    }

    [Fact]
    public void Contains_ReturnsFalse_ForMismatchedAddressFamily()
    {
        var mask = NetworkMask.Parse("192.168.0.0/16");

        Assert.False(mask.Contains(IPAddress.IPv6Loopback));
    }

    [Fact]
    public void Matcher_AllowsAll_WhenStarProvided()
    {
        var matcher = new NetworkMaskMatcher(new[] { "*" });

        Assert.False(matcher.IsEmpty);
        Assert.True(matcher.IsAllowed(IPAddress.Parse("203.0.113.10")));
        Assert.True(matcher.IsAllowed(IPAddress.IPv6Loopback));
    }

    [Fact]
    public void Matcher_ReturnsFalse_WhenNoMasksConfigured()
    {
        var matcher = new NetworkMaskMatcher(Array.Empty<string>());

        Assert.True(matcher.IsEmpty);
        Assert.False(matcher.IsAllowed(IPAddress.Parse("127.0.0.1")));
        Assert.False(matcher.IsAllowed(null));
    }

    [Fact]
    public void Matcher_SupportsIpv4AndIpv6Masks()
    {
        var matcher = new NetworkMaskMatcher(new[] { "192.168.0.0/24", "::1/128" });

        Assert.True(matcher.IsAllowed(IPAddress.Parse("192.168.0.42")));
        Assert.False(matcher.IsAllowed(IPAddress.Parse("10.0.0.1")));
        Assert.True(matcher.IsAllowed(IPAddress.IPv6Loopback));
        Assert.False(matcher.IsAllowed(IPAddress.IPv6Any));
    }

    [Fact]
    public void Matcher_Throws_ForInvalidEntries()
    {
        var exception = Assert.Throws<FormatException>(() => new NetworkMaskMatcher(new[] { "invalid-mask" }));
        Assert.Contains("invalid-mask", exception.Message, StringComparison.OrdinalIgnoreCase);
    }
}
