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

namespace ZB.MOM.WW.ScadaBridge.Security;

/// <summary>
/// Config shape for trusted-proxy forwarded-header handling
/// (<c>ScadaBridge:Security:ForwardedHeaders</c>). Disabled by default: a deployment
/// with no reverse proxy must NOT honor X-Forwarded-For (a client could spoof its
/// throttle key). The documented Traefik topologies enable it with the docker
/// network range so LoginThrottle keys on the REAL client IP (arch-review R2 N2).
/// </summary>
public sealed class ForwardedHeadersSecurityOptions
{
    /// <summary>Configuration section binding this options class.</summary>
    public const string SectionName = "ScadaBridge:Security:ForwardedHeaders";

    /// <summary>Whether X-Forwarded-For from trusted proxies is honored.</summary>
    public bool Enabled { get; set; }

    /// <summary>Exact proxy IPs allowed to supply X-Forwarded-For.</summary>
    public string[] KnownProxies { get; set; } = [];

    /// <summary>CIDR networks allowed to supply X-Forwarded-For (e.g. "172.16.0.0/12").</summary>
    public string[] KnownNetworks { get; set; } = [];
}

/// <summary>
/// Builds the <see cref="ForwardedHeadersOptions"/> for <c>app.UseForwardedHeaders</c>.
/// Pure and fail-fast: malformed entries throw at startup rather than silently
/// trusting nothing/everything. Defaults (loopback) are CLEARED — only the
/// explicitly configured proxies/networks are trusted, and ForwardLimit=1 means
/// only the entry the trusted proxy itself appended is honored.
/// </summary>
public static class ForwardedHeadersSetup
{
    /// <summary>
    /// Builds the forwarded-headers options, or <see langword="null"/> when disabled.
    /// </summary>
    /// <param name="options">The bound <see cref="ForwardedHeadersSecurityOptions"/>.</param>
    /// <returns>The configured <see cref="ForwardedHeadersOptions"/>, or <see langword="null"/> when disabled.</returns>
    public static ForwardedHeadersOptions? Build(ForwardedHeadersSecurityOptions options)
    {
        if (!options.Enabled) return null;
        var built = new ForwardedHeadersOptions
        {
            ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
            ForwardLimit = 1,
        };
        built.KnownProxies.Clear();
        // KnownIPNetworks (System.Net.IPNetwork) is the net10 successor to the now-deprecated
        // KnownNetworks; UseForwardedHeaders reads it identically.
        built.KnownIPNetworks.Clear();
        foreach (var proxy in options.KnownProxies)
            built.KnownProxies.Add(IPAddress.Parse(proxy));
        foreach (var network in options.KnownNetworks)
        {
            var parts = network.Split('/');
            built.KnownIPNetworks.Add(new(IPAddress.Parse(parts[0]), int.Parse(parts[1])));
        }
        return built;
    }
}
