package com.zb.mom.ww.mxgateway.client;

import java.nio.file.Path;
import java.time.Duration;
import java.util.Objects;

/**
 * Immutable configuration for {@link MxGatewayClient} and
 * {@link GalaxyRepositoryClient}.
 *
 * <p>Captures the gateway endpoint, API key, transport security selection and
 * call/stream timeouts. Instances are constructed via {@link #builder()}.
 */
public final class MxGatewayClientOptions {
    private static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofSeconds(10);
    private static final Duration DEFAULT_CALL_TIMEOUT = Duration.ofSeconds(30);
    private static final int DEFAULT_MAX_GRPC_MESSAGE_BYTES = 16 * 1024 * 1024;

    private final String endpoint;
    private final String apiKey;
    private final boolean plaintext;
    private final Path caCertificatePath;
    private final boolean requireCertificateValidation;
    private final String serverNameOverride;
    private final Duration connectTimeout;
    private final Duration callTimeout;
    private final Duration streamTimeout;
    private final int maxGrpcMessageBytes;

    private MxGatewayClientOptions(Builder builder) {
        endpoint = requireText(builder.endpoint, "endpoint");
        apiKey = builder.apiKey == null ? "" : builder.apiKey;
        plaintext = builder.plaintext;
        caCertificatePath = builder.caCertificatePath;
        requireCertificateValidation = builder.requireCertificateValidation;
        serverNameOverride = builder.serverNameOverride == null ? "" : builder.serverNameOverride;
        connectTimeout = builder.connectTimeout == null ? DEFAULT_CONNECT_TIMEOUT : builder.connectTimeout;
        callTimeout = builder.callTimeout == null ? DEFAULT_CALL_TIMEOUT : builder.callTimeout;
        streamTimeout = builder.streamTimeout;
        maxGrpcMessageBytes = builder.maxGrpcMessageBytes <= 0
                ? DEFAULT_MAX_GRPC_MESSAGE_BYTES
                : builder.maxGrpcMessageBytes;
    }

    /**
     * Returns a fresh builder with default timeouts and no endpoint set.
     *
     * @return a new {@link Builder}
     */
    public static Builder builder() {
        return new Builder();
    }

    /**
     * Returns the configured gRPC target endpoint.
     *
     * @return the endpoint string in {@code host:port} or DNS-target form
     */
    public String endpoint() {
        return endpoint;
    }

    /**
     * Returns the configured API key, or an empty string if none was supplied.
     *
     * @return the raw API key
     */
    public String apiKey() {
        return apiKey;
    }

    /**
     * Returns the API key with the body redacted, safe to write to logs.
     *
     * @return the redacted form produced by {@link MxGatewaySecrets#redactApiKey(String)}
     */
    public String redactedApiKey() {
        return MxGatewaySecrets.redactApiKey(apiKey);
    }

    /**
     * Returns whether the client is configured to use plaintext transport.
     *
     * @return {@code true} for plaintext, {@code false} for TLS
     */
    public boolean plaintext() {
        return plaintext;
    }

    /**
     * Returns the configured CA certificate file used to verify the gateway,
     * or {@code null} when the platform trust store is used.
     *
     * @return the CA certificate path, or {@code null}
     */
    public Path caCertificatePath() {
        return caCertificatePath;
    }

    /**
     * Returns whether TLS certificate verification is required even when no CA is pinned.
     * When {@code false} (default), the gateway's self-signed certificate is accepted
     * without verification. When {@code true}, the OS trust store is used.
     * Pinning a CA via {@link #caCertificatePath()} always verifies regardless of this flag.
     *
     * @return {@code true} if strict certificate verification is required
     */
    public boolean requireCertificateValidation() {
        return requireCertificateValidation;
    }

    /**
     * Returns the TLS server-name override, or an empty string when none was supplied.
     *
     * @return the server-name override
     */
    public String serverNameOverride() {
        return serverNameOverride;
    }

    /**
     * Returns the channel connect timeout.
     *
     * @return the connect timeout duration
     */
    public Duration connectTimeout() {
        return connectTimeout;
    }

    /**
     * Returns the per-call deadline applied to unary RPCs.
     *
     * @return the call timeout duration
     */
    public Duration callTimeout() {
        return callTimeout;
    }

    /**
     * Returns the deadline applied to server-streaming RPCs, or {@code null} when none is set.
     *
     * @return the stream timeout duration, or {@code null}
     */
    public Duration streamTimeout() {
        return streamTimeout;
    }

    public int maxGrpcMessageBytes() {
        return maxGrpcMessageBytes;
    }

    @Override
    public String toString() {
        return "MxGatewayClientOptions{"
                + "endpoint='"
                + endpoint
                + '\''
                + ", apiKey='"
                + redactedApiKey()
                + '\''
                + ", plaintext="
                + plaintext
                + ", caCertificatePath="
                + caCertificatePath
                + ", requireCertificateValidation="
                + requireCertificateValidation
                + ", serverNameOverride='"
                + serverNameOverride
                + '\''
                + ", connectTimeout="
                + connectTimeout
                + ", callTimeout="
                + callTimeout
                + ", streamTimeout="
                + streamTimeout
                + ", maxGrpcMessageBytes="
                + maxGrpcMessageBytes
                + '}';
    }

    private static String requireText(String value, String name) {
        if (value == null || value.isBlank()) {
            throw new IllegalArgumentException(name + " is required");
        }
        return value;
    }

    /**
     * Mutable builder for {@link MxGatewayClientOptions}.
     */
    public static final class Builder {
        private String endpoint;
        private String apiKey;
        private boolean plaintext;
        private Path caCertificatePath;
        private boolean requireCertificateValidation;
        private String serverNameOverride;
        private Duration connectTimeout;
        private Duration callTimeout;
        private Duration streamTimeout;
        private int maxGrpcMessageBytes;

        private Builder() {
        }

        /**
         * Sets the gRPC target endpoint.
         *
         * @param value endpoint in {@code host:port} or DNS-target form; required
         * @return this builder
         */
        public Builder endpoint(String value) {
            endpoint = value;
            return this;
        }

        /**
         * Sets the API key sent in the {@code authorization} header.
         *
         * @param value the API key, or {@code null}/blank to disable authentication
         * @return this builder
         */
        public Builder apiKey(String value) {
            apiKey = value;
            return this;
        }

        /**
         * Selects plaintext transport instead of TLS.
         *
         * @param value {@code true} for plaintext, {@code false} for TLS
         * @return this builder
         */
        public Builder plaintext(boolean value) {
            plaintext = value;
            return this;
        }

        /**
         * Sets the CA certificate used to verify the gateway server.
         *
         * @param value path to a PEM-encoded CA certificate, or {@code null} to use the platform trust store
         * @return this builder
         */
        public Builder caCertificatePath(Path value) {
            caCertificatePath = value;
            return this;
        }

        /**
         * When {@code true}, TLS connections without a pinned CA use the OS trust store
         * and will reject the gateway's self-signed certificate. When {@code false}
         * (default), the gateway certificate is accepted without verification —
         * appropriate for this internal tool's auto-generated self-signed certificate.
         * Pinning a CA via {@link #caCertificatePath(Path)} always verifies.
         *
         * @param value {@code true} to require certificate validation, {@code false} to accept any cert
         * @return this builder
         */
        public Builder requireCertificateValidation(boolean value) {
            requireCertificateValidation = value;
            return this;
        }

        /**
         * Overrides the TLS server name used during the handshake.
         *
         * @param value the override host name, or empty/{@code null} for none
         * @return this builder
         */
        public Builder serverNameOverride(String value) {
            serverNameOverride = value;
            return this;
        }

        /**
         * Sets the channel connect timeout.
         *
         * @param value the connect timeout, must be non-{@code null}
         * @return this builder
         * @throws NullPointerException if {@code value} is {@code null}
         */
        public Builder connectTimeout(Duration value) {
            connectTimeout = Objects.requireNonNull(value, "connectTimeout");
            return this;
        }

        /**
         * Sets the per-call deadline applied to unary RPCs.
         *
         * @param value the call timeout, must be non-{@code null}
         * @return this builder
         * @throws NullPointerException if {@code value} is {@code null}
         */
        public Builder callTimeout(Duration value) {
            callTimeout = Objects.requireNonNull(value, "callTimeout");
            return this;
        }

        /**
         * Sets the deadline applied to server-streaming RPCs.
         *
         * @param value the stream timeout, must be non-{@code null}
         * @return this builder
         * @throws NullPointerException if {@code value} is {@code null}
         */
        public Builder streamTimeout(Duration value) {
            streamTimeout = Objects.requireNonNull(value, "streamTimeout");
            return this;
        }

        public Builder maxGrpcMessageBytes(int value) {
            maxGrpcMessageBytes = value;
            return this;
        }

        /**
         * Builds an immutable {@link MxGatewayClientOptions} from the current state.
         *
         * @return a new options instance
         * @throws IllegalArgumentException if {@code endpoint} was not set or is blank
         */
        public MxGatewayClientOptions build() {
            return new MxGatewayClientOptions(this);
        }
    }
}
