using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Configuration;

namespace MemberCenter.Infrastructure.Configuration;

public static class TrustedForwardedHeaders
{
    public static void Configure(ForwardedHeadersOptions options, IConfiguration configuration)
    {
        options.KnownNetworks.Clear();
        options.KnownProxies.Clear();

        foreach (var value in Split(configuration["ReverseProxy:KnownProxies"]))
        {
            if (!IPAddress.TryParse(value, out var address))
            {
                throw new InvalidOperationException($"ReverseProxy:KnownProxies contains an invalid IP address: {value}");
            }

            options.KnownProxies.Add(address);
        }

        foreach (var value in Split(configuration["ReverseProxy:KnownNetworks"]))
        {
            var parts = value.Split('/', 2, StringSplitOptions.TrimEntries);
            if (parts.Length != 2 ||
                !IPAddress.TryParse(parts[0], out var address) ||
                !int.TryParse(parts[1], out var prefixLength) ||
                prefixLength < 1 ||
                prefixLength > (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork ? 32 : 128))
            {
                throw new InvalidOperationException($"ReverseProxy:KnownNetworks contains an invalid CIDR: {value}");
            }

            options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(address, prefixLength));
        }

        var hasTrustedProxy = options.KnownProxies.Count > 0 || options.KnownNetworks.Count > 0;
        options.ForwardedHeaders = hasTrustedProxy
            ? ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            : ForwardedHeaders.None;
        options.ForwardLimit = Math.Clamp(configuration.GetValue<int?>("ReverseProxy:ForwardLimit") ?? 1, 1, 5);
    }

    private static IEnumerable<string> Split(string? value) =>
        (value ?? string.Empty).Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
}
