/*
 * Copyright 1999-2021 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.client.auth.impl.process;

import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.client.auth.impl.NacosAuthLoginConstant;
import com.alibaba.nacos.client.utils.ClientBasicParamUtil;
import com.alibaba.nacos.client.utils.ContextPathUtil;
import com.alibaba.nacos.common.http.HttpRestResult;
import com.alibaba.nacos.common.http.client.NacosRestTemplate;
import com.alibaba.nacos.common.http.param.Header;
import com.alibaba.nacos.common.http.param.Query;
import com.alibaba.nacos.common.utils.InternetAddressUtil;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext;
import com.fasterxml.jackson.databind.JsonNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTPS_PREFIX;
import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX;

/**
 * Login processor for Http.
 *
 * @author Nacos
 */
public class HttpLoginProcessor implements LoginProcessor {
    
    private static final Logger SECURITY_LOGGER = LoggerFactory.getLogger(HttpLoginProcessor.class);
    
    private static final String LOGIN_V1_URL = "/v1/auth/users/login";
    
    private static final String LOGIN_V3_URL = "/v3/auth/user/login";
    
    public static final String DEFAULT_NACOS_WEB_CONTEXT = "/nacos";
    
    private final NacosRestTemplate nacosRestTemplate;
    
    public HttpLoginProcessor(NacosRestTemplate nacosRestTemplate) {
        this.nacosRestTemplate = nacosRestTemplate;
    }
    
    @Override
    public LoginIdentityContext getResponse(Properties properties) {
        
        String contextPath = ContextPathUtil.normalizeContextPath(
                properties.getProperty(PropertyKeyConst.CONTEXT_PATH, DEFAULT_NACOS_WEB_CONTEXT));
        String server = properties.getProperty(NacosAuthLoginConstant.SERVER, StringUtils.EMPTY);
        
        if (!server.startsWith(HTTPS_PREFIX) && !server.startsWith(HTTP_PREFIX)) {
            if (!InternetAddressUtil.containsPort(server)) {
                server = server + InternetAddressUtil.IP_PORT_SPLITER + ClientBasicParamUtil.getDefaultServerPort();
            }
            server = HTTP_PREFIX + server;
        }
        
        String url = server + contextPath + LOGIN_V3_URL;
        
        Map<String, String> params = new HashMap<>(2);
        Map<String, String> bodyMap = new HashMap<>(2);
        params.put(PropertyKeyConst.USERNAME, properties.getProperty(PropertyKeyConst.USERNAME, StringUtils.EMPTY));
        bodyMap.put(PropertyKeyConst.PASSWORD, properties.getProperty(PropertyKeyConst.PASSWORD, StringUtils.EMPTY));
        try {
            HttpRestResult<String> restResult = nacosRestTemplate.postForm(url, Header.EMPTY,
                    Query.newInstance().initParams(params), bodyMap, String.class);
            int code = restResult.getCode();
            if (code == NacosException.NOT_FOUND || code == NacosException.SERVER_NOT_IMPLEMENTED) {
                url = server + contextPath + LOGIN_V1_URL;
                restResult = nacosRestTemplate.postForm(url, Header.EMPTY, Query.newInstance().initParams(params),
                        bodyMap, String.class);
            }
            if (!restResult.ok()) {
                SECURITY_LOGGER.error("login failed: {}", JacksonUtils.toJson(restResult));
                return null;
            }
            JsonNode obj = JacksonUtils.toObj(restResult.getData());
            
            LoginIdentityContext loginIdentityContext = new LoginIdentityContext();
            
            if (obj.has(Constants.ACCESS_TOKEN)) {
                loginIdentityContext.setParameter(NacosAuthLoginConstant.ACCESSTOKEN,
                        obj.get(Constants.ACCESS_TOKEN).asText());
                loginIdentityContext.setParameter(NacosAuthLoginConstant.TOKENTTL,
                        obj.get(Constants.TOKEN_TTL).asText());
            } else {
                SECURITY_LOGGER.info("[NacosClientAuthServiceImpl] ACCESS_TOKEN is empty from response");
            }
            return loginIdentityContext;
        } catch (Exception e) {
            Map<String, String> newBodyMap = new HashMap<>(bodyMap);
            newBodyMap.put(PropertyKeyConst.PASSWORD,
                    ClientBasicParamUtil.desensitiseParameter(bodyMap.get(PropertyKeyConst.PASSWORD)));
            SECURITY_LOGGER.error("[NacosClientAuthServiceImpl] login http request failed"
                    + " url: {}, params: {}, bodyMap: {}, errorMsg: {}", url, params, newBodyMap, e.getMessage());
            return null;
        }
    }
    
}
