/*
 * Copyright (c) 2019 the Eclipse Milo Authors
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 */

package org.eclipse.milo.opcua.stack.server;

import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;

import org.eclipse.milo.opcua.stack.core.channel.EncodingLimits;
import org.eclipse.milo.opcua.stack.core.security.CertificateManager;
import org.eclipse.milo.opcua.stack.core.security.TrustListManager;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
import org.eclipse.milo.opcua.stack.core.types.structured.ApplicationDescription;
import org.eclipse.milo.opcua.stack.server.security.ServerCertificateValidator;

public interface UaStackServerConfig {

    /**
     * @return the {@link EndpointConfiguration}s for this server.
     */
    Set<EndpointConfiguration> getEndpoints();

    /**
     * Get the application name for the server.
     * <p/>
     * This will be used in the {@link ApplicationDescription} returned to clients.
     *
     * @return the application name for the server.
     */
    LocalizedText getApplicationName();

    /**
     * Get the application uri for the server.
     * <p/>
     * This will be used in the {@link ApplicationDescription} returned to clients.
     * <p/>
     * <b>The application uri must match the application uri used on the server's application instance certificate.</b>
     *
     * @return the application uri for the server.
     */
    String getApplicationUri();

    /**
     * Get the product uri for the server.
     * <p/>
     * This will be used in the {@link ApplicationDescription} returned to clients.
     *
     * @return the product uri for the server.
     */
    String getProductUri();

    /**
     * @return the configured {@link EncodingLimits}.
     */
    EncodingLimits getEncodingLimits();

    /**
     * @return the minimum allowable secure channel lifetime, in milliseconds.
     */
    UInteger getMinimumSecureChannelLifetime();

    /**
     * @return the maximum allowable secure channel lifetime, in milliseconds.
     */
    UInteger getMaximumSecureChannelLifetime();

    /**
     * @return the {@link CertificateManager} for this server.
     */
    CertificateManager getCertificateManager();

    /**
     * @return the {@link TrustListManager} for this server.
     */
    TrustListManager getTrustListManager();

    /**
     * @return the {@link ServerCertificateValidator} for this server.
     */
    ServerCertificateValidator getCertificateValidator();

    /**
     * @return the {@link KeyPair} used for SSL/TLS with HTTPS endpoints.
     */
    Optional<KeyPair> getHttpsKeyPair();

    /**
     * @return the {@link X509Certificate} used for SSL/TLS with HTTPS endpoints.
     * @deprecated This will only return the leaf certificate, use @{{@link #getHttpsCertificateChain()}} to get the
     * full chain.
     */
    @Deprecated
    default Optional<X509Certificate> getHttpsCertificate() {
        return getHttpsCertificateChain().flatMap(chain -> Optional.ofNullable(chain[0]));
    }

    /**
     * @return the {@link X509Certificate} used for SSL/TLS with HTTPS endpoints.
     */
    Optional<X509Certificate[]> getHttpsCertificateChain();

    /**
     * @return the {@link ExecutorService} for this server.
     */
    ExecutorService getExecutor();

    /**
     * @return a new {@link UaStackServerConfigBuilder}.
     */
    static UaStackServerConfigBuilder builder() {
        return new UaStackServerConfigBuilder();
    }

    /**
     * Copy the values from an existing {@link UaStackServerConfig} into a new {@link UaStackServerConfigBuilder}.
     * <p>
     * This builder can be used to make any desired modifications before invoking
     * {@link UaStackServerConfigBuilder#build()} to produce a new config.
     *
     * @param config the {@link UaStackServerConfig} to copy from.
     * @return a {@link UaStackServerConfigBuilder} pre-populated with values from {@code config}.
     */
    static UaStackServerConfigBuilder copy(UaStackServerConfig config) {
        UaStackServerConfigBuilder builder = builder();

        builder.setEndpoints(config.getEndpoints());
        builder.setApplicationName(config.getApplicationName());
        builder.setApplicationUri(config.getApplicationUri());
        builder.setProductUri(config.getProductUri());
        builder.setEncodingLimits(config.getEncodingLimits());
        builder.setMinimumSecureChannelLifetime(config.getMinimumSecureChannelLifetime());
        builder.setMaximumSecureChannelLifetime(config.getMaximumSecureChannelLifetime());
        builder.setCertificateManager(config.getCertificateManager());
        builder.setTrustListManager(config.getTrustListManager());
        builder.setCertificateValidator(config.getCertificateValidator());
        builder.setHttpsKeyPair(config.getHttpsKeyPair().orElse(null));
        builder.setHttpsCertificateChain(config.getHttpsCertificateChain().orElse(null));
        builder.setExecutor(config.getExecutor());

        return builder;
    }

    /**
     * Copy the values from an existing {@link UaStackServerConfig} into a new {@link UaStackServerConfigBuilder}
     * and then submit the builder to the provided consumer for modification.
     *
     * @param config   the {@link UaStackServerConfig} to copy from.
     * @param consumer a {@link Consumer} that may modify the builder.
     * @return a {@link UaStackServerConfig} built from the builder provided to {@code consumer}.
     */
    static UaStackServerConfig copy(
        UaStackServerConfig config,
        Consumer<UaStackServerConfigBuilder> consumer) {

        UaStackServerConfigBuilder builder = copy(config);

        consumer.accept(builder);

        return builder.build();
    }

}
