// Copyright 2012-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/opts.go in the NATS server Go source.

using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using ZB.MOM.NatsNet.Server.Auth;

namespace ZB.MOM.NatsNet.Server;

/// <summary>
/// Write timeout behavior policy.
/// Mirrors <c>WriteTimeoutPolicy</c> in opts.go.
/// </summary>
public enum WriteTimeoutPolicy : byte
{
    Default = 0,
    Close = 1,
    Retry = 2,
}

/// <summary>
/// Store encryption cipher selection.
/// Mirrors <c>StoreCipher</c> in opts.go.
/// </summary>
public enum StoreCipher
{
    ChaCha = 0,
    Aes = 1,
    NoCipher = 2,
}

/// <summary>
/// OCSP stapling mode.
/// Mirrors <c>OCSPMode</c> in opts.go.
/// </summary>
public enum OcspMode : byte
{
    Auto = 0,
    Always = 1,
    Never = 2,
    Must = 3,
}

/// <summary>
/// Set of pinned certificate SHA256 hashes (lowercase hex-encoded DER SubjectPublicKeyInfo).
/// Mirrors <c>PinnedCertSet</c> in opts.go.
/// </summary>
public class PinnedCertSet : HashSet<string>
{
    public PinnedCertSet() : base(StringComparer.OrdinalIgnoreCase)
    {
    }

    public PinnedCertSet(IEnumerable<string> collection) : base(collection, StringComparer.OrdinalIgnoreCase)
    {
    }
}

/// <summary>
/// Compression options for route/leaf connections.
/// Mirrors <c>CompressionOpts</c> in opts.go.
/// </summary>
public class CompressionOpts
{
    public string Mode { get; set; } = string.Empty;
    public List<TimeSpan> RttThresholds { get; set; } = [];
}

/// <summary>
/// Compression mode string constants.
/// </summary>
public static class CompressionModes
{
    public const string Off = "off";
    public const string Accept = "accept";
    public const string S2Fast = "s2_fast";
    public const string S2Better = "s2_better";
    public const string S2Best = "s2_best";
    public const string S2Auto = "s2_auto";
}

/// <summary>
/// TLS configuration parsed from config file.
/// Mirrors <c>TLSConfigOpts</c> in opts.go.
/// </summary>
public class TlsConfigOpts
{
    public string CertFile { get; set; } = string.Empty;
    public string KeyFile { get; set; } = string.Empty;
    public string CaFile { get; set; } = string.Empty;
    public bool Verify { get; set; }
    public bool Insecure { get; set; }
    public bool Map { get; set; }
    public bool TlsCheckKnownUrls { get; set; }
    public bool HandshakeFirst { get; set; }
    public TimeSpan FallbackDelay { get; set; }
    public double Timeout { get; set; }
    public long RateLimit { get; set; }
    public bool AllowInsecureCiphers { get; set; }
    public List<TlsCipherSuite> Ciphers { get; set; } = [];
    public List<SslApplicationProtocol> CurvePreferences { get; set; } = [];
    public PinnedCertSet? PinnedCerts { get; set; }
    public string CertMatch { get; set; } = string.Empty;
    public bool CertMatchSkipInvalid { get; set; }
    public List<string> CaCertsMatch { get; set; } = [];
    public List<TlsCertPairOpt> Certificates { get; set; } = [];
    public SslProtocols MinVersion { get; set; }
    public Auth.CertificateIdentityProvider.OcspPeerConfig? OcspPeerConfig { get; set; }
}

/// <summary>
/// Certificate and key file pair.
/// Mirrors <c>TLSCertPairOpt</c> in opts.go.
/// </summary>
public class TlsCertPairOpt
{
    public string CertFile { get; set; } = string.Empty;
    public string KeyFile { get; set; } = string.Empty;
}

/// <summary>
/// OCSP stapling configuration.
/// Mirrors <c>OCSPConfig</c> in opts.go.
/// </summary>
public class OcspConfig
{
    public OcspMode Mode { get; set; }
    public List<string> OverrideUrls { get; set; } = [];
}

/// <summary>
/// OCSP response cache configuration.
/// Mirrors <c>OCSPResponseCacheConfig</c> in opts.go.
/// </summary>
public class OcspResponseCacheConfig
{
    public string Type { get; set; } = string.Empty;
    public string LocalStore { get; set; } = string.Empty;
    public bool PreserveRevoked { get; set; }
    public double SaveInterval { get; set; }
}

/// <summary>
/// Cluster configuration options.
/// Mirrors <c>ClusterOpts</c> in opts.go.
/// </summary>
public class ClusterOpts
{
    public string Name { get; set; } = string.Empty;
    public string Host { get; set; } = string.Empty;
    public int Port { get; set; }
    public string Username { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
    public double AuthTimeout { get; set; }
    public double TlsTimeout { get; set; }
    public SslServerAuthenticationOptions? TlsConfig { get; set; }
    public bool TlsMap { get; set; }
    public bool TlsCheckKnownUrls { get; set; }
    public PinnedCertSet? TlsPinnedCerts { get; set; }
    public bool TlsHandshakeFirst { get; set; }
    public TimeSpan TlsHandshakeFirstFallback { get; set; }
    public string ListenStr { get; set; } = string.Empty;
    public string Advertise { get; set; } = string.Empty;
    public bool NoAdvertise { get; set; }
    public int ConnectRetries { get; set; }
    public bool ConnectBackoff { get; set; }
    public int PoolSize { get; set; }
    public List<string> PinnedAccounts { get; set; } = [];
    public CompressionOpts Compression { get; set; } = new();
    public RoutePermissions? Permissions { get; set; }
    public TimeSpan PingInterval { get; set; }
    public int MaxPingsOut { get; set; }
    public TimeSpan WriteDeadline { get; set; }
    public WriteTimeoutPolicy WriteTimeout { get; set; }
    internal TlsConfigOpts? TlsConfigOpts { get; set; }
}

/// <summary>
/// Gateway configuration options.
/// Mirrors <c>GatewayOpts</c> in opts.go.
/// </summary>
public class GatewayOpts
{
    public string Name { get; set; } = string.Empty;
    public string Host { get; set; } = string.Empty;
    public int Port { get; set; }
    public string Username { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
    public double AuthTimeout { get; set; }
    public SslServerAuthenticationOptions? TlsConfig { get; set; }
    public double TlsTimeout { get; set; }
    public bool TlsMap { get; set; }
    public bool TlsCheckKnownUrls { get; set; }
    public PinnedCertSet? TlsPinnedCerts { get; set; }
    public string Advertise { get; set; } = string.Empty;
    public int ConnectRetries { get; set; }
    public bool ConnectBackoff { get; set; }
    public List<RemoteGatewayOpts> Gateways { get; set; } = [];
    public bool RejectUnknown { get; set; }
    public TimeSpan WriteDeadline { get; set; }
    public WriteTimeoutPolicy WriteTimeout { get; set; }
    internal TlsConfigOpts? TlsConfigOpts { get; set; }
}

/// <summary>
/// Remote gateway connection options.
/// Mirrors <c>RemoteGatewayOpts</c> in opts.go.
/// </summary>
public class RemoteGatewayOpts
{
    public string Name { get; set; } = string.Empty;
    public SslServerAuthenticationOptions? TlsConfig { get; set; }
    public double TlsTimeout { get; set; }
    public List<Uri> Urls { get; set; } = [];
    internal TlsConfigOpts? TlsConfigOpts { get; set; }

    internal RemoteGatewayOpts Clone()
    {
        return new RemoteGatewayOpts
        {
            Name = Name,
            TlsConfig = TlsConfig,
            TlsTimeout = TlsTimeout,
            TlsConfigOpts = TlsConfigOpts,
            Urls = [.. Urls],
        };
    }
}

/// <summary>
/// Leaf node configuration options.
/// Mirrors <c>LeafNodeOpts</c> in opts.go.
/// </summary>
public class LeafNodeOpts
{
    public string Host { get; set; } = string.Empty;
    public int Port { get; set; }
    public string Username { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
    public bool ProxyRequired { get; set; }
    public string Nkey { get; set; } = string.Empty;
    public string Account { get; set; } = string.Empty;
    public List<User>? Users { get; set; }
    public double AuthTimeout { get; set; }
    public SslServerAuthenticationOptions? TlsConfig { get; set; }
    public double TlsTimeout { get; set; }
    public bool TlsMap { get; set; }
    public PinnedCertSet? TlsPinnedCerts { get; set; }
    public bool TlsHandshakeFirst { get; set; }
    public TimeSpan TlsHandshakeFirstFallback { get; set; }
    public string Advertise { get; set; } = string.Empty;
    public bool NoAdvertise { get; set; }
    public TimeSpan ReconnectInterval { get; set; }
    public TimeSpan WriteDeadline { get; set; }
    public WriteTimeoutPolicy WriteTimeout { get; set; }
    public CompressionOpts Compression { get; set; } = new();
    public List<RemoteLeafOpts> Remotes { get; set; } = [];
    public string MinVersion { get; set; } = string.Empty;
    public bool IsolateLeafnodeInterest { get; set; }
    internal TlsConfigOpts? TlsConfigOpts { get; set; }
}

/// <summary>
/// Signature handler delegate for NKey authentication.
/// Mirrors <c>SignatureHandler</c> in opts.go.
/// </summary>
public delegate (string jwt, byte[] signature, Exception? err) SignatureHandler(byte[] nonce);

/// <summary>
/// Options for connecting to a remote leaf node.
/// Mirrors <c>RemoteLeafOpts</c> in opts.go.
/// </summary>
public class RemoteLeafOpts
{
    public string LocalAccount { get; set; } = string.Empty;
    public bool NoRandomize { get; set; }
    public List<Uri> Urls { get; set; } = [];
    public string Credentials { get; set; } = string.Empty;
    public string Nkey { get; set; } = string.Empty;
    public SignatureHandler? SignatureCb { get; set; }
    public bool Tls { get; set; }
    public SslServerAuthenticationOptions? TlsConfig { get; set; }
    public double TlsTimeout { get; set; }
    public bool TlsHandshakeFirst { get; set; }
    public bool Hub { get; set; }
    public List<string> DenyImports { get; set; } = [];
    public List<string> DenyExports { get; set; } = [];
    public TimeSpan FirstInfoTimeout { get; set; }
    public CompressionOpts Compression { get; set; } = new();
    public RemoteLeafWebsocketOpts Websocket { get; set; } = new();
    public RemoteLeafProxyOpts Proxy { get; set; } = new();
    public bool JetStreamClusterMigrate { get; set; }
    public TimeSpan JetStreamClusterMigrateDelay { get; set; }
    public bool LocalIsolation { get; set; }
    public bool RequestIsolation { get; set; }
    public bool Disabled { get; set; }
    internal TlsConfigOpts? TlsConfigOpts { get; set; }
}

/// <summary>WebSocket sub-options for a remote leaf connection.</summary>
public class RemoteLeafWebsocketOpts
{
    public bool Compression { get; set; }
    public bool NoMasking { get; set; }
}

/// <summary>HTTP proxy sub-options for a remote leaf connection.</summary>
public class RemoteLeafProxyOpts
{
    public string Url { get; set; } = string.Empty;
    public string Username { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
    public TimeSpan Timeout { get; set; }
}

/// <summary>
/// WebSocket configuration options.
/// Mirrors <c>WebsocketOpts</c> in opts.go.
/// </summary>
public class WebsocketOpts
{
    public string Host { get; set; } = string.Empty;
    public int Port { get; set; }
    public string Advertise { get; set; } = string.Empty;
    public string NoAuthUser { get; set; } = string.Empty;
    public string JwtCookie { get; set; } = string.Empty;
    public string UsernameCookie { get; set; } = string.Empty;
    public string PasswordCookie { get; set; } = string.Empty;
    public string TokenCookie { get; set; } = string.Empty;
    public string Username { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
    public string Token { get; set; } = string.Empty;
    public double AuthTimeout { get; set; }
    public bool NoTls { get; set; }
    public SslServerAuthenticationOptions? TlsConfig { get; set; }
    public bool TlsMap { get; set; }
    public PinnedCertSet? TlsPinnedCerts { get; set; }
    public bool SameOrigin { get; set; }
    public List<string> AllowedOrigins { get; set; } = [];
    public bool Compression { get; set; }
    public TimeSpan HandshakeTimeout { get; set; }
    public TimeSpan PingInterval { get; set; }
    public Dictionary<string, string> Headers { get; set; } = new();
    internal TlsConfigOpts? TlsConfigOpts { get; set; }
}

/// <summary>
/// MQTT configuration options.
/// Mirrors <c>MQTTOpts</c> in opts.go.
/// </summary>
public class MqttOpts
{
    public string Host { get; set; } = string.Empty;
    public int Port { get; set; }
    public string NoAuthUser { get; set; } = string.Empty;
    public string Username { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
    public string Token { get; set; } = string.Empty;
    public string JsDomain { get; set; } = string.Empty;
    public int StreamReplicas { get; set; }
    public int ConsumerReplicas { get; set; }
    public bool ConsumerMemoryStorage { get; set; }
    public TimeSpan ConsumerInactiveThreshold { get; set; }
    public double AuthTimeout { get; set; }
    public SslServerAuthenticationOptions? TlsConfig { get; set; }
    public bool TlsMap { get; set; }
    public double TlsTimeout { get; set; }
    public PinnedCertSet? TlsPinnedCerts { get; set; }
    public TimeSpan AckWait { get; set; }
    public TimeSpan JsApiTimeout { get; set; }
    public ushort MaxAckPending { get; set; }
    internal TlsConfigOpts? TlsConfigOpts { get; set; }
    internal bool RejectQoS2Pub { get; set; }
    internal bool DowngradeQoS2Sub { get; set; }
}

/// <summary>
/// JetStream server-level limits.
/// Mirrors <c>JSLimitOpts</c> in opts.go.
/// </summary>
public class JsLimitOpts
{
    public int MaxRequestBatch { get; set; }
    public int MaxAckPending { get; set; }
    public int MaxHaAssets { get; set; }
    public TimeSpan Duplicates { get; set; }
    public int MaxBatchInflightPerStream { get; set; }
    public int MaxBatchInflightTotal { get; set; }
    public int MaxBatchSize { get; set; }
    public TimeSpan MaxBatchTimeout { get; set; }
}

/// <summary>
/// TPM configuration for JetStream encryption.
/// Mirrors <c>JSTpmOpts</c> in opts.go.
/// </summary>
public class JsTpmOpts
{
    public string KeysFile { get; set; } = string.Empty;
    public string KeyPassword { get; set; } = string.Empty;
    public string SrkPassword { get; set; } = string.Empty;
    public int Pcr { get; set; }
}

/// <summary>
/// Auth callout options for external authentication.
/// Mirrors <c>AuthCallout</c> in opts.go.
/// </summary>
public class AuthCalloutOpts
{
    public string Issuer { get; set; } = string.Empty;
    public string Account { get; set; } = string.Empty;
    public List<string> AuthUsers { get; set; } = [];
    public string XKey { get; set; } = string.Empty;
    public List<string> AllowedAccounts { get; set; } = [];
}

/// <summary>
/// Proxy configuration for trusted proxies.
/// Mirrors <c>ProxiesConfig</c> in opts.go.
/// </summary>
public class ProxiesConfig
{
    public List<ProxyConfig> Trusted { get; set; } = [];
}

/// <summary>
/// A single trusted proxy identified by public key.
/// Mirrors <c>ProxyConfig</c> in opts.go.
/// </summary>
public class ProxyConfig
{
    public string Key { get; set; } = string.Empty;
}

/// <summary>
/// Parsed authorization section from config file.
/// Mirrors the unexported <c>authorization</c> struct in opts.go.
/// </summary>
public class AuthorizationConfig
{
    public string User { get; set; } = string.Empty;
    public string Pass { get; set; } = string.Empty;
    public string Token { get; set; } = string.Empty;
    public string Nkey { get; set; } = string.Empty;
    public string Acc { get; set; } = string.Empty;
    public bool ProxyRequired { get; set; }
    public double Timeout { get; set; }
    public AuthCalloutOpts? Callout { get; set; }
}

/// <summary>
/// Custom authentication interface.
/// Mirrors the <c>Authentication</c> interface in auth.go.
/// </summary>
public interface IAuthentication
{
    bool Check(IClientAuthentication client);
}

/// <summary>
/// Client-side of authentication check.
/// Mirrors <c>ClientAuthentication</c> in auth.go.
/// </summary>
public interface IClientAuthentication
{
    string? GetOpts();
    bool IsTls();
    string? GetTlsConnectionState();
    string RemoteAddress();
}

// IAccountResolver is defined in Accounts/AccountResolver.cs.
