/*
 * Copyright 1999-2020 Alibaba Group Holding Ltd.
 *
 * Licensed 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 com.alibaba.nacos.naming.pojo.instance;

import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.CommonParams;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.builder.InstanceBuilder;
import com.alibaba.nacos.common.spi.NacosServiceLoader;
import com.alibaba.nacos.common.utils.ConvertUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.core.utils.WebUtils;
import com.alibaba.nacos.naming.constants.Constants;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;

import jakarta.servlet.http.HttpServletRequest;
import java.util.Collection;

/**
 * Http instance builder.
 *
 * <p>
 * The http openAPI will split each attributes of {@link Instance} as parameters of http parameters. This Builder can
 * set an http request and get necessary parameters to build {@link Instance}.
 * </p>
 *
 * <p>
 * This builder is a wrapper for {@link com.alibaba.nacos.api.naming.pojo.builder.InstanceBuilder} and will inject some
 * extension handler by spi.
 * </p>
 *
 * @author xiweng.yy
 */
public class HttpRequestInstanceBuilder {
    
    private final InstanceBuilder actualBuilder;
    
    private final Collection<InstanceExtensionHandler> handlers;
    
    private boolean defaultInstanceEphemeral = true;
    
    private HttpRequestInstanceBuilder() {
        this.actualBuilder = InstanceBuilder.newBuilder();
        this.handlers = NacosServiceLoader.newServiceInstances(InstanceExtensionHandler.class);
    }
    
    public static HttpRequestInstanceBuilder newBuilder() {
        return new HttpRequestInstanceBuilder();
    }
    
    /**
     * Build a new {@link Instance} and chain handled by {@link InstanceExtensionHandler}.
     *
     * @return new instance
     */
    public Instance build() {
        Instance result = actualBuilder.build();
        for (InstanceExtensionHandler each : handlers) {
            each.handleExtensionInfo(result);
        }
        setInstanceId(result);
        return result;
    }
    
    public HttpRequestInstanceBuilder setDefaultInstanceEphemeral(boolean defaultInstanceEphemeral) {
        this.defaultInstanceEphemeral = defaultInstanceEphemeral;
        return this;
    }
    
    public HttpRequestInstanceBuilder setRequest(HttpServletRequest request) throws NacosException {
        for (InstanceExtensionHandler each : handlers) {
            each.configExtensionInfoFromRequest(request);
        }
        setAttributesToBuilder(request);
        return this;
    }
    
    private void setAttributesToBuilder(HttpServletRequest request) throws NacosException {
        actualBuilder.setServiceName(WebUtils.required(request, CommonParams.SERVICE_NAME));
        actualBuilder.setIp(WebUtils.required(request, "ip"));
        actualBuilder.setPort(Integer.parseInt(WebUtils.required(request, "port")));
        actualBuilder.setHealthy(ConvertUtils.toBoolean(WebUtils.optional(request, "healthy", "true")));
        actualBuilder.setEphemeral(ConvertUtils
                .toBoolean(WebUtils.optional(request, "ephemeral", String.valueOf(defaultInstanceEphemeral))));
        setWeight(request);
        setCluster(request);
        setEnabled(request);
        setMetadata(request);
    }
    
    private void setWeight(HttpServletRequest request) throws NacosException {
        double weight = Double.parseDouble(WebUtils.optional(request, "weight", "1"));
        if (weight > Constants.MAX_WEIGHT_VALUE || weight < Constants.MIN_WEIGHT_VALUE) {
            throw new NacosException(NacosException.INVALID_PARAM,
                    "instance format invalid: The weights range from " + Constants.MIN_WEIGHT_VALUE + " to "
                            + Constants.MAX_WEIGHT_VALUE);
        }
        actualBuilder.setWeight(weight);
    }
    
    private void setCluster(HttpServletRequest request) {
        String cluster = WebUtils.optional(request, CommonParams.CLUSTER_NAME, StringUtils.EMPTY);
        if (StringUtils.isBlank(cluster)) {
            cluster = WebUtils.optional(request, "cluster", UtilsAndCommons.DEFAULT_CLUSTER_NAME);
        }
        actualBuilder.setClusterName(cluster);
    }
    
    private void setEnabled(HttpServletRequest request) {
        String enabledString = WebUtils.optional(request, "enabled", StringUtils.EMPTY);
        boolean enabled;
        if (StringUtils.isBlank(enabledString)) {
            enabled = ConvertUtils.toBoolean(WebUtils.optional(request, "enable", "true"));
        } else {
            enabled = ConvertUtils.toBoolean(enabledString);
        }
        actualBuilder.setEnabled(enabled);
    }
    
    private void setMetadata(HttpServletRequest request) throws NacosException {
        String metadata = WebUtils.optional(request, "metadata", StringUtils.EMPTY);
        if (StringUtils.isNotEmpty(metadata)) {
            actualBuilder.setMetadata(UtilsAndCommons.parseMetadata(metadata));
        }
    }
    
    private void setInstanceId(Instance instance) {
        instance.setInstanceId(InstanceIdGeneratorManager.generateInstanceId(instance));
    }
}
