using System.Net;
using MBN_STOCK_WEBVIEW.Playout.Diagnostics;

namespace MBN_STOCK_WEBVIEW.Playout.Tests;

public sealed class PgmDiagnosticSafetyProbeTests
{
    private const int Port = 30_001;
    private const int TargetProcessId = 101;
    private const int CompetingProcessId = 202;

    [Theory]
    [InlineData("127.0.0.1", "127.0.0.1")]
    [InlineData("127.0.0.1", "0.0.0.0")]
    [InlineData("::1", "::1")]
    [InlineData("::1", "::")]
    public void TargetOwnedExactOrWildcardListener_IsAccepted(
        string requestedAddress,
        string listenerAddress)
    {
        var queried = Evaluate(
            requestedAddress,
            [Entry(listenerAddress, TargetProcessId)],
            out var isOwned);

        Assert.True(queried);
        Assert.True(isOwned);
    }

    [Theory]
    [InlineData("127.0.0.1", "127.0.0.1", "0.0.0.0")]
    [InlineData("127.0.0.1", "0.0.0.0", "127.0.0.1")]
    [InlineData("::1", "::1", "::")]
    [InlineData("::1", "::", "::1")]
    public void MatchingEndpointWithCompetingOwner_FailsClosedEvenAfterTargetRow(
        string requestedAddress,
        string targetAddress,
        string competingAddress)
    {
        var queried = Evaluate(
            requestedAddress,
            [
                Entry(targetAddress, TargetProcessId),
                Entry(competingAddress, CompetingProcessId)
            ],
            out var isOwned);

        Assert.True(queried);
        Assert.False(isOwned);
    }

    [Theory]
    [InlineData("127.0.0.1", "127.0.0.2")]
    [InlineData("::1", "::2")]
    public void DifferentLocalAddressOwnedByCompetitor_DoesNotCompete(
        string requestedAddress,
        string unrelatedAddress)
    {
        var queried = Evaluate(
            requestedAddress,
            [
                Entry(requestedAddress, TargetProcessId),
                Entry(unrelatedAddress, CompetingProcessId)
            ],
            out var isOwned);

        Assert.True(queried);
        Assert.True(isOwned);
    }

    [Theory]
    [InlineData("127.0.0.1", "127.0.0.2")]
    [InlineData("::1", "::2")]
    public void TargetOwnedDifferentLocalAddress_IsNotAccepted(
        string requestedAddress,
        string differentAddress)
    {
        var queried = Evaluate(
            requestedAddress,
            [Entry(differentAddress, TargetProcessId)],
            out var isOwned);

        Assert.True(queried);
        Assert.False(isOwned);
    }

    [Theory]
    [InlineData("127.0.0.1", "::")]
    [InlineData("::1", "0.0.0.0")]
    public void OtherAddressFamilyWildcard_DoesNotMatchRequestedEndpoint(
        string requestedAddress,
        string otherFamilyWildcard)
    {
        var queried = Evaluate(
            requestedAddress,
            [Entry(otherFamilyWildcard, TargetProcessId)],
            out var isOwned);

        Assert.True(queried);
        Assert.False(isOwned);
    }

    [Theory]
    [InlineData("0.0.0.0")]
    [InlineData("192.0.2.1")]
    [InlineData("::")]
    [InlineData("2001:db8::1")]
    public void RequestedAddressMustBeLoopback(string requestedAddress)
    {
        var queried = Evaluate(
            requestedAddress,
            [Entry(requestedAddress, TargetProcessId)],
            out var isOwned);

        Assert.False(queried);
        Assert.False(isOwned);
    }

    [Theory]
    [InlineData("127.0.0.1", "127.0.0.1")]
    [InlineData("::1", "::1")]
    public void MatchingEndpointOwnedOnlyByCompetitor_IsRejected(
        string requestedAddress,
        string competingAddress)
    {
        var queried = Evaluate(
            requestedAddress,
            [Entry(competingAddress, CompetingProcessId)],
            out var isOwned);

        Assert.True(queried);
        Assert.False(isOwned);
    }

    private static bool Evaluate(
        string requestedAddress,
        IReadOnlyList<TcpListenerOwnershipEntry> listeners,
        out bool isOwned) =>
        WindowsTcpListenerOwnershipProbe.TryEvaluateOwnership(
            IPAddress.Parse(requestedAddress),
            Port,
            TargetProcessId,
            listeners,
            out isOwned);

    private static TcpListenerOwnershipEntry Entry(
        string address,
        int processId) =>
        new(IPAddress.Parse(address), Port, processId);
}
