package com.sifang.modules.sys.util.http;

import com.google.gson.Gson;
import com.itextpdf.text.pdf.codec.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.web.client.RestTemplate;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;

public class GinlongTemplateBase extends EzTemplateBase {

    public static final String keyId = "1300386381676623064";
    public static final String keySecret = "48b6adf9df9546e39176e9b0f020b95c";
    private static final String apiUrl = "https://api.ginlong.com:13333/";
    private static final Logger log = LoggerFactory.getLogger(GinlongTemplateBase.class);
    private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";

    public GinlongTemplateBase(RestTemplate restTemplate) {
        super(restTemplate);
    }

    protected <T, K> T doPostJson(String url, ParameterizedTypeReference<T> responseType, HttpEntity<K> entity, Object[] uriVariables) {
        HttpHeaders headers = writableHeaders(entity.getHeaders());
        String body = new Gson().toJson(entity.getBody());
        callHeaders(url, body, headers);
        HttpEntity<String> data = new HttpEntity<>(body, headers);
        return restTemplate.exchange(url, HttpMethod.POST, data, responseType, uriVariables).getBody();
    }

    protected <T, K> T doPostJson(String url, Class<T> responseType, HttpEntity<K> entity, Object[] uriVariables) {
        HttpHeaders headers = writableHeaders(entity.getHeaders());
        String body = new Gson().toJson(entity.getBody());
        callHeaders(url, body, headers);
        return restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(body, headers), responseType, uriVariables).getBody();
    }

    public static String getGMTTime() {
        Calendar cd = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        return sdf.format(cd.getTime());
    }

    public static byte[] genHMAC(String data, String key) {
        try {
            SecretKeySpec signinKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), HMAC_SHA1_ALGORITHM);
            Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
            mac.init(signinKey);
            return mac.doFinal(data.getBytes());
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            throw new RuntimeException(e);
        }
    }

    public static String getAuthorization(String contentMd5, String date, String url, String apiSecret, String apiId) {
        log.info("get authorization, content.md5: {}, date: {}, url: {}", contentMd5, date, url);
        StringBuilder sb = new StringBuilder();
        sb.append("POST\n");
        sb.append(contentMd5).append("\n");
        sb.append("application/json\n");
        sb.append(date).append("\n");
        sb.append(url.substring("https://api.ginlong.com:13333".length()));
        byte[] bytes = genHMAC(sb.toString(), apiSecret);
        return "API " + apiId + ":" + Base64.encodeBytes(bytes);
    }

    public static String getDigest(String test) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(test.getBytes());
            byte[] b = md.digest();
            return Base64.encodeBytes(b);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    protected <K> void callHeaders(String url, String data, HttpHeaders headers) {
        String md5 = getDigest(data);
        String gmtTime = getGMTTime();
        headers.set("Authorization", getAuthorization(md5, gmtTime, url, keySecret, keyId));
        headers.set("Date", gmtTime);
        headers.set("Content-MD5", md5);
    }

    private HttpHeaders writableHeaders(HttpHeaders headers) {
        HttpHeaders ret = new HttpHeaders();
        Set<Map.Entry<String, List<String>>> entries = headers.entrySet();
        for (Map.Entry<String, List<String>> entry : entries) {
            ret.put(entry.getKey(), entry.getValue());
        }
        return ret;
    }

}
