/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 *
 */

package org.apache.skywalking.oap.server.receiver.sharing.server;

import java.util.Objects;
import org.apache.logging.log4j.util.Strings;
import org.apache.skywalking.oap.server.core.CoreModule;
import org.apache.skywalking.oap.server.core.remote.health.HealthCheckServiceHandler;
import org.apache.skywalking.oap.server.core.server.GRPCHandlerRegister;
import org.apache.skywalking.oap.server.core.server.GRPCHandlerRegisterImpl;
import org.apache.skywalking.oap.server.core.server.HTTPHandlerRegister;
import org.apache.skywalking.oap.server.core.server.HTTPHandlerRegisterImpl;
import org.apache.skywalking.oap.server.core.server.auth.AuthenticationInterceptor;
import org.apache.skywalking.oap.server.library.module.ModuleDefine;
import org.apache.skywalking.oap.server.library.module.ModuleProvider;
import org.apache.skywalking.oap.server.library.module.ModuleStartException;
import org.apache.skywalking.oap.server.library.server.ServerException;
import org.apache.skywalking.oap.server.library.server.grpc.GRPCServer;
import org.apache.skywalking.oap.server.library.server.http.HTTPServer;
import org.apache.skywalking.oap.server.library.server.http.HTTPServerConfig;
import org.apache.skywalking.oap.server.library.util.StringUtil;

public class SharingServerModuleProvider extends ModuleProvider {

    private SharingServerConfig config;
    private GRPCServer grpcServer;
    private HTTPServer httpServer;
    private ReceiverGRPCHandlerRegister receiverGRPCHandlerRegister;
    private ReceiverHTTPHandlerRegister receiverHTTPHandlerRegister;
    private AuthenticationInterceptor authenticationInterceptor;

    @Override
    public String name() {
        return "default";
    }

    @Override
    public Class<? extends ModuleDefine> module() {
        return SharingServerModule.class;
    }

    @Override
    public ConfigCreator newConfigCreator() {
        return new ConfigCreator<SharingServerConfig>() {
            @Override
            public Class type() {
                return SharingServerConfig.class;
            }

            @Override
            public void onInitialized(final SharingServerConfig initialized) {
                config = initialized;
            }
        };
    }

    @Override
    public void prepare() {
        if (config.getRestPort() > 0) {
            HTTPServerConfig httpServerConfig =
                HTTPServerConfig.builder()
                                .host(config.getRestHost()).port(config.getRestPort())
                                .contextPath(config.getRestContextPath())
                                .maxThreads(config.getRestMaxThreads())
                                .acceptQueueSize(config.getRestAcceptQueueSize())
                                .idleTimeOut(config.getRestIdleTimeOut())
                                .maxRequestHeaderSize(config.getHttpMaxRequestHeaderSize()).build();
            httpServerConfig.setHost(Strings.isBlank(config.getRestHost()) ? "0.0.0.0" : config.getRestHost());
            httpServerConfig.setPort(config.getRestPort());
            httpServerConfig.setContextPath(config.getRestContextPath());

            httpServer = new HTTPServer(httpServerConfig);
            httpServer.initialize();

            this.registerServiceImplementation(HTTPHandlerRegister.class, new HTTPHandlerRegisterImpl(httpServer));
        } else {
            this.receiverHTTPHandlerRegister = new ReceiverHTTPHandlerRegister();
            this.registerServiceImplementation(HTTPHandlerRegister.class, receiverHTTPHandlerRegister);
        }

        if (StringUtil.isNotEmpty(config.getAuthentication())) {
            authenticationInterceptor = new AuthenticationInterceptor(config.getAuthentication());
        }

        if (config.getGRPCPort() != 0) {
            if (config.isGRPCSslEnabled()) {
                grpcServer = new GRPCServer(
                    Strings.isBlank(config.getGRPCHost()) ? "0.0.0.0" : config.getGRPCHost(),
                    config.getGRPCPort(),
                    config.getGRPCSslCertChainPath(),
                    config.getGRPCSslKeyPath(),
                    config.getGRPCSslTrustedCAsPath()
                );
            } else {
                grpcServer = new GRPCServer(
                    Strings.isBlank(config.getGRPCHost()) ? "0.0.0.0" : config.getGRPCHost(),
                    config.getGRPCPort()
                );
            }
            if (config.getMaxMessageSize() > 0) {
                grpcServer.setMaxMessageSize(config.getMaxMessageSize());
            }
            if (config.getMaxConcurrentCallsPerConnection() > 0) {
                grpcServer.setMaxConcurrentCallsPerConnection(config.getMaxConcurrentCallsPerConnection());
            }
            if (config.getGRPCThreadPoolQueueSize() > 0) {
                grpcServer.setThreadPoolQueueSize(config.getGRPCThreadPoolQueueSize());
            }
            if (config.getGRPCThreadPoolSize() > 0) {
                grpcServer.setThreadPoolSize(config.getGRPCThreadPoolSize());
            }
            grpcServer.initialize();

            GRPCHandlerRegisterImpl grpcHandlerRegister = new GRPCHandlerRegisterImpl(grpcServer);
            if (Objects.nonNull(authenticationInterceptor)) {
                grpcHandlerRegister.addFilter(authenticationInterceptor);
            }
            this.registerServiceImplementation(GRPCHandlerRegister.class, grpcHandlerRegister);
        } else {
            this.receiverGRPCHandlerRegister = new ReceiverGRPCHandlerRegister();
            if (Objects.nonNull(authenticationInterceptor)) {
                receiverGRPCHandlerRegister.addFilter(authenticationInterceptor);
            }
            this.registerServiceImplementation(GRPCHandlerRegister.class, receiverGRPCHandlerRegister);
        }
    }

    @Override
    public void start() {
        if (Objects.nonNull(grpcServer)) {
            grpcServer.addHandler(new HealthCheckServiceHandler());
        }

        if (Objects.nonNull(receiverGRPCHandlerRegister)) {
            receiverGRPCHandlerRegister.setGrpcHandlerRegister(getManager().find(CoreModule.NAME)
                                                                           .provider()
                                                                           .getService(GRPCHandlerRegister.class));
        }
        if (Objects.nonNull(receiverHTTPHandlerRegister)) {
            receiverHTTPHandlerRegister.setHttpHandlerRegister(getManager().find(CoreModule.NAME)
                                                                           .provider()
                                                                           .getService(HTTPHandlerRegister.class));
        }
    }

    @Override
    public void notifyAfterCompleted() throws ModuleStartException {
        try {
            if (Objects.nonNull(grpcServer)) {
                grpcServer.start();
            }
            if (Objects.nonNull(httpServer)) {
                httpServer.start();
            }
        } catch (ServerException e) {
            throw new ModuleStartException(e.getMessage(), e);
        }
    }

    @Override
    public String[] requiredModules() {
        return new String[] {CoreModule.NAME};
    }
}
