// Copyright 2016-2025 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Adapted from server/ciphersuites.go in the NATS server Go source.

using System.Net.Security;
using System.Security.Authentication;

namespace ZB.MOM.NatsNet.Server.Auth;

/// <summary>
/// TLS cipher suite and curve preference definitions.
/// Mirrors Go <c>ciphersuites.go</c> — cipherMap, defaultCipherSuites, curvePreferenceMap,
/// defaultCurvePreferences.
/// </summary>
public static class CipherSuites
{
    /// <summary>
    /// Map of cipher suite names to their <see cref="TlsCipherSuite"/> values.
    /// Populated at static init time — mirrors Go <c>init()</c> + <c>cipherMap</c>.
    /// </summary>
    public static IReadOnlyDictionary<string, TlsCipherSuite> CipherMap { get; }

    /// <summary>
    /// Reverse map of cipher suite ID to name.
    /// Mirrors Go <c>cipherMapByID</c>.
    /// </summary>
    public static IReadOnlyDictionary<TlsCipherSuite, string> CipherMapById { get; }

    static CipherSuites()
    {
        // .NET does not have a direct equivalent of Go's tls.CipherSuites() /
        // tls.InsecureCipherSuites() enumeration.  We enumerate the well-known
        // TLS 1.2 and 1.3 cipher suites defined in the TlsCipherSuite enum.
        var byName = new Dictionary<string, TlsCipherSuite>(StringComparer.OrdinalIgnoreCase);
        var byId = new Dictionary<TlsCipherSuite, string>();

        foreach (TlsCipherSuite cs in Enum.GetValues(typeof(TlsCipherSuite)))
        {
            var name = cs.ToString();
            byName.TryAdd(name, cs);
            byId.TryAdd(cs, name);
        }

        CipherMap = byName;
        CipherMapById = byId;
    }

    /// <summary>
    /// Compatibility init hook for PortTracker parity with Go <c>init()</c>.
    /// Safe and idempotent.
    /// </summary>
    public static void Init()
    {
        _ = CipherMap;
        _ = CipherMapById;
    }

    /// <summary>
    /// Returns the default set of TLS 1.2 cipher suites.
    /// .NET manages cipher suite selection at the OS/SChannel/OpenSSL level;
    /// this list provides the preferred suites for configuration alignment with Go.
    /// Mirrors Go <c>defaultCipherSuites</c>.
    /// </summary>
    public static TlsCipherSuite[] DefaultCipherSuites()
    {
        // Return commonly-used TLS 1.2 cipher suites in preference order.
        // TLS 1.3 suites are always enabled in .NET and cannot be individually toggled.
        return
        [
            TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
            TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
            TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
            TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
            TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
            TlsCipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
        ];
    }

    /// <summary>
    /// Supported named curve / key exchange preferences.
    /// Mirrors Go <c>curvePreferenceMap</c>.
    /// </summary>
    public static IReadOnlyDictionary<string, SslApplicationProtocol> CurvePreferenceMap { get; } =
        new Dictionary<string, SslApplicationProtocol>(StringComparer.OrdinalIgnoreCase)
        {
            // .NET does not expose individual curve selection in the same way as Go.
            // These entries exist for configuration-file compatibility and mapping.
            // Actual curve negotiation is handled by the OS TLS stack.
            ["X25519"] = new SslApplicationProtocol("X25519"),
            ["CurveP256"] = new SslApplicationProtocol("CurveP256"),
            ["CurveP384"] = new SslApplicationProtocol("CurveP384"),
            ["CurveP521"] = new SslApplicationProtocol("CurveP521"),
        };

    /// <summary>
    /// Returns the default curve preferences, ordered highest security first.
    /// Mirrors Go <c>defaultCurvePreferences</c>.
    /// </summary>
    public static string[] DefaultCurvePreferences()
    {
        return
        [
            "X25519",
            "CurveP256",
            "CurveP384",
            "CurveP521",
        ];
    }
}
