/*
 * 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.ram.utils;

import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.client.auth.ram.identify.CredentialService;
import com.alibaba.nacos.common.codec.Base64;
import com.alibaba.nacos.common.utils.StringUtils;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * adapt spas interface.
 *
 * @author Nacos
 */
public class SpasAdapter {
    
    private static final String TIMESTAMP_HEADER = "Timestamp";
    
    private static final String SIGNATURE_HEADER = "Spas-Signature";
    
    private static final String GROUP_KEY = "group";
    
    public static final String TENANT_KEY = "tenant";
    
    private static final String SHA_ENCRYPT = "HmacSHA1";
    
    public static Map<String, String> getSignHeaders(String resource, String secretKey) {
        Map<String, String> header = new HashMap<>(2);
        String timeStamp = String.valueOf(System.currentTimeMillis());
        header.put(TIMESTAMP_HEADER, timeStamp);
        if (secretKey != null) {
            String signature;
            if (StringUtils.isBlank(resource)) {
                signature = signWithHmacSha1Encrypt(timeStamp, secretKey);
            } else {
                signature = signWithHmacSha1Encrypt(resource + "+" + timeStamp, secretKey);
            }
            header.put(SIGNATURE_HEADER, signature);
        }
        return header;
    }
    
    public static Map<String, String> getSignHeaders(String groupKey, String tenant, String secretKey) {
        if (StringUtils.isBlank(groupKey) && StringUtils.isBlank(tenant)) {
            return null;
        }
        
        String resource = "";
        if (StringUtils.isNotBlank(groupKey) && StringUtils.isNotBlank(tenant)) {
            resource = tenant + "+" + groupKey;
        } else {
            if (!StringUtils.isBlank(groupKey)) {
                resource = groupKey;
            }
        }
        return getSignHeaders(resource, secretKey);
    }
    
    public static Map<String, String> getSignHeaders(Map<String, String> paramValues, String secretKey) {
        if (null == paramValues) {
            return null;
        }
        
        String resource = "";
        if (paramValues.containsKey(TENANT_KEY) && paramValues.containsKey(GROUP_KEY)) {
            resource = paramValues.get(TENANT_KEY) + "+" + paramValues.get(GROUP_KEY);
        } else {
            if (!StringUtils.isBlank(paramValues.get(GROUP_KEY))) {
                resource = paramValues.get(GROUP_KEY);
            }
        }
        return getSignHeaders(resource, secretKey);
    }
    
    public static String getSk() {
        return CredentialService.getInstance().getCredential().getSecretKey();
    }
    
    public static String getAk() {
        return CredentialService.getInstance().getCredential().getAccessKey();
    }
    
    public static void freeCredentialInstance() {
        CredentialService.freeInstance();
    }
    
    /**
     * Sign with hmac SHA1 encrtpt.
     *
     * @param encryptText encrypt text
     * @param encryptKey  encrypt key
     * @return base64 string
     */
    public static String signWithHmacSha1Encrypt(String encryptText, String encryptKey) {
        try {
            byte[] data = encryptKey.getBytes(Constants.ENCODE);
            // Construct a key according to the given byte array, and the second parameter specifies the name of a key algorithm
            SecretKey secretKey = new SecretKeySpec(data, SHA_ENCRYPT);
            // Generate a Mac object specifying Mac algorithm
            Mac mac = Mac.getInstance(SHA_ENCRYPT);
            // Initialize the Mac object with the given key
            mac.init(secretKey);
            byte[] text = encryptText.getBytes(Constants.ENCODE);
            byte[] textFinal = mac.doFinal(text);
            // Complete Mac operation, base64 encoding, convert byte array to string
            return new String(Base64.encodeBase64(textFinal), Constants.ENCODE);
        } catch (Exception e) {
            throw new RuntimeException("signWithhmacSHA1Encrypt fail", e);
        }
    }
}
