namespace NATS.Server.WebSocket;

/// <summary>
/// Separate TLS configuration for the WebSocket listener,
/// allowing different certificates than the main NATS listener.
/// Go reference: server/websocket.go — wsTLSConfig.
/// </summary>
public sealed class WebSocketTlsConfig
{
    public string? CertFile { get; set; }
    public string? KeyFile { get; set; }
    public string? CaFile { get; set; }
    public bool RequireClientCert { get; set; }
    public bool InsecureSkipVerify { get; set; }

    /// <summary>Returns true when a certificate file has been specified.</summary>
    public bool IsConfigured => !string.IsNullOrWhiteSpace(CertFile);

    /// <summary>
    /// Validates the TLS configuration.
    /// An empty configuration (no cert, no key) is valid and means no TLS.
    /// When either cert or key is specified, both must be provided.
    /// </summary>
    public WebSocketTlsValidationResult Validate()
    {
        var errors = new List<string>();

        bool hasCert = !string.IsNullOrWhiteSpace(CertFile);
        bool hasKey = !string.IsNullOrWhiteSpace(KeyFile);

        if (hasKey && !hasCert)
            errors.Add("CertFile must be specified when KeyFile is set.");

        if (hasCert && !hasKey)
            errors.Add("KeyFile must be specified when CertFile is set.");

        return new WebSocketTlsValidationResult(errors.Count == 0, errors);
    }

    /// <summary>
    /// Returns true when this configuration differs from <paramref name="other"/>.
    /// Used for hot-reload diff detection.
    /// </summary>
    public bool HasChangedFrom(WebSocketTlsConfig? other)
    {
        if (other is null)
            return true;

        return CertFile != other.CertFile
            || KeyFile != other.KeyFile
            || CaFile != other.CaFile
            || RequireClientCert != other.RequireClientCert
            || InsecureSkipVerify != other.InsecureSkipVerify;
    }

    /// <summary>
    /// Creates a <see cref="WebSocketTlsConfig"/> from the WebSocket-specific TLS fields
    /// in <paramref name="options"/>, or returns <c>null</c> if no WebSocket TLS is configured.
    /// </summary>
    public static WebSocketTlsConfig? FromOptions(NatsOptions options)
    {
        var ws = options.WebSocket;

        if (string.IsNullOrWhiteSpace(ws.TlsCert) && string.IsNullOrWhiteSpace(ws.TlsKey))
            return null;

        return new WebSocketTlsConfig
        {
            CertFile = ws.TlsCert,
            KeyFile = ws.TlsKey,
        };
    }
}

/// <summary>Result of <see cref="WebSocketTlsConfig.Validate"/>.</summary>
public sealed record WebSocketTlsValidationResult(
    bool IsValid,
    IReadOnlyList<string> Errors);
