using Microsoft.Extensions.Logging;

namespace ZB.MOM.WW.MxGateway.Client;

/// <summary>
/// Configures the gRPC channel used by the .NET MXAccess Gateway client.
/// </summary>
public sealed class MxGatewayClientOptions
{
    /// <summary>
    /// Gets the gateway endpoint URI (required).
    /// </summary>
    public required Uri Endpoint { get; init; }

    /// <summary>
    /// Gets the API key for gateway authentication (required).
    /// </summary>
    public required string ApiKey { get; init; }

    /// <summary>
    /// Gets a value indicating whether to use TLS for the gateway connection.
    /// </summary>
    public bool UseTls { get; init; }

    /// <summary>
    /// Gets the path to a CA certificate file for custom certificate validation.
    /// </summary>
    public string? CaCertificatePath { get; init; }

    /// <summary>
    /// When true, TLS connections without a pinned <see cref="CaCertificatePath"/>
    /// use the OS trust store. When false (default), the gateway certificate is
    /// accepted without verification — appropriate for this internal tool's
    /// auto-generated self-signed certificate. Pinning a CA always verifies.
    /// </summary>
    public bool RequireCertificateValidation { get; init; }

    /// <summary>
    /// Gets the server name override for SNI during TLS handshake.
    /// </summary>
    public string? ServerNameOverride { get; init; }

    /// <summary>
    /// Gets the timeout for establishing connection to the gateway.
    /// </summary>
    public TimeSpan ConnectTimeout { get; init; } = TimeSpan.FromSeconds(10);

    /// <summary>
    /// Gets the default timeout for unary gRPC calls.
    /// </summary>
    public TimeSpan DefaultCallTimeout { get; init; } = TimeSpan.FromSeconds(30);

    /// <summary>
    /// Gets the optional timeout for streaming gRPC calls.
    /// </summary>
    public TimeSpan? StreamTimeout { get; init; }

    /// <summary>
    /// Gets the maximum size in bytes for gRPC messages.
    /// </summary>
    public int MaxGrpcMessageBytes { get; init; } = 16 * 1024 * 1024;

    /// <summary>
    /// Gets the retry configuration for safe unary calls.
    /// </summary>
    public MxGatewayClientRetryOptions Retry { get; init; } = new();

    /// <summary>
    /// Gets the logger factory for diagnostic logging.
    /// </summary>
    public ILoggerFactory? LoggerFactory { get; init; }

    /// <summary>
    /// Validates the client options for consistency and correctness.
    /// </summary>
    /// <exception cref="ArgumentNullException">Endpoint is null.</exception>
    /// <exception cref="ArgumentException">Options are invalid or inconsistent.</exception>
    /// <exception cref="ArgumentOutOfRangeException">Timeout values are not greater than zero.</exception>
    public void Validate()
    {
        ArgumentNullException.ThrowIfNull(Endpoint);

        if (!Endpoint.IsAbsoluteUri)
        {
            throw new ArgumentException(
                "The gateway endpoint must be an absolute URI.",
                nameof(Endpoint));
        }

        if (string.IsNullOrWhiteSpace(ApiKey))
        {
            throw new ArgumentException(
                "The gateway API key must not be empty.",
                nameof(ApiKey));
        }

        if (ConnectTimeout <= TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException(
                nameof(ConnectTimeout),
                "The connect timeout must be greater than zero.");
        }

        if (DefaultCallTimeout <= TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException(
                nameof(DefaultCallTimeout),
                "The default call timeout must be greater than zero.");
        }

        if (StreamTimeout is not null && StreamTimeout <= TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException(
                nameof(StreamTimeout),
                "The stream timeout must be greater than zero when configured.");
        }

        if (MaxGrpcMessageBytes <= 0)
        {
            throw new ArgumentOutOfRangeException(
                nameof(MaxGrpcMessageBytes),
                "The maximum gRPC message size must be greater than zero.");
        }

        if (UseTls && Endpoint.Scheme != Uri.UriSchemeHttps)
        {
            throw new ArgumentException(
                "UseTls requires an https gateway endpoint.",
                nameof(Endpoint));
        }

        if (!UseTls && Endpoint.Scheme == Uri.UriSchemeHttps)
        {
            throw new ArgumentException(
                "An https gateway endpoint requires UseTls.",
                nameof(Endpoint));
        }

        Retry.Validate();
    }
}
