using Microsoft.AspNetCore.Http;
using NetTools;
using System;
using System.Linq;
using System.Net;
using System.Collections.Generic;
using Ablera.Serdica.Authentication.Utilities;
using Ablera.Serdica.Authentication.Models.Oidc;
using Ablera.Serdica.Common.Tools.Utilities;

namespace Ablera.Serdica.Authority.Extensions;

public static class AllowedMaskExtensions
{
    // Lazily built the first time AllowedMaskExtensions is referenced.
    private static readonly IReadOnlyCollection<IPAddressRange> AssociatedNetworks = ListeningNetworksRetriever.Retrieve();

    public static AllowedMask? MergeWith(this AllowedMask? client, AllowedMask? global)
        => (client, global) switch
        {
            (null, null) => null,
            (null, _) => global,
            _ => new()
            {
                SameNetworks = client.SameNetworks ?? global?.SameNetworks,
                Networks = client.Networks ?? global?.Networks,
                Hosts = client.Hosts ?? global?.Hosts,
                Ports = client.Ports ?? global?.Ports
            }
        };

    public static bool MatchesRemote(this AllowedMask allow, HttpContext http)
    {
        var remoteIp = http.Connection.RemoteIpAddress ?? IPAddress.None;
        var host = http.Request.Host.Host;
        var port = http.Request.Host.Port ?? 0;

        bool ipOk = allow.Networks == null ||
                      allow.Networks.Any(net => IPAddressRange.Parse(net).Contains(remoteIp));

        bool hostOk = allow.Hosts == null ||
                      allow.Hosts.Any(h => StringComparer.OrdinalIgnoreCase.Equals(h, host));

        bool portOk = allow.Ports == null || allow.Ports.Contains(port);

        // Same-network rule: only enforced when SameNetwork == true
        bool sameNetworkOk =
            allow.SameNetworks != true ||         // Flag not enabled → no restriction
            AssociatedNetworks == null ||         // Could not determine our own network
            AssociatedNetworks.Any(network => network.Contains(remoteIp));

        return ipOk && hostOk && portOk && sameNetworkOk;
    }
}
