package com.e6yun.project.sdk.api.core;

import com.e6yun.project.sdk.api.constant.CodeConstant;
import com.e6yun.project.sdk.api.constant.FormatConstant;
import com.e6yun.project.sdk.api.constant.ParamConstant;
import com.e6yun.project.sdk.api.exception.ApiException;
import com.e6yun.project.sdk.api.utils.OkhttpClientUtils;
import com.e6yun.project.sdk.api.utils.SignUtils;
import com.google.gson.*;
import okhttp3.HttpUrl;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

public class E6SdkHttp {
    /**
     * 服务器地址
     */
    private String address;

    /**
     * 公钥
     */
    private String appKey;

    /**
     * 私钥
     */
    private String secretKey;

    /**
     * 格式时间
     */
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 构造函数
     * @param address
     * @param appKey
     * @param secretKey
     * @throws IllegalArgumentException
     */
    public E6SdkHttp(String address, String appKey, String secretKey) throws IllegalArgumentException{
        if(StringUtils.isEmpty(address)){
            throw new IllegalArgumentException("服务器地址不能为空");
        }
        if(StringUtils.isEmpty(appKey)){
            throw new IllegalArgumentException("公钥不能为空");
        }
        if(StringUtils.isEmpty(secretKey)){
            throw new IllegalArgumentException("私钥不能为空");
        }
        this.appKey = appKey;
        this.secretKey = secretKey;
        this.address = address;
    }

    /**
     * 获取当前时间
     * @return
     */
    private static String timestamp(){
        Instant instant = Instant.now();
        // 因为服务器的时区是东八区，所以客户端计算出的时间必须和服务器时区匹配
        final String SERVER_ZONE_ID = "+08:00";
        ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of(SERVER_ZONE_ID));
        String timestamp = DATE_TIME_FORMATTER.format(zonedDateTime);
        return timestamp;
    }

    /**
     * 构建Url地址
     * @param path
     * @return
     * @throws URISyntaxException
     */
    private String buildUrl(String path) throws URISyntaxException {
        HttpUrl.Builder huBuilder = HttpUrl.parse(this.address).newBuilder();
        String url = huBuilder.encodedPath(path).build().url().toString();
        return url;
    }

    /**
     * 补全参数
     * @param param
     */
    private void completeParam(Map<String, String> param){
        param.put(ParamConstant.APP_KEY, this.appKey);
        param.put(ParamConstant.FORMAT, FormatConstant.JSON);
        param.put(ParamConstant.TIMESTAMP, timestamp());
        String sign = SignUtils.sign(this.appKey, this.secretKey, param);
        param.put(ParamConstant.SIGN, sign.toUpperCase());
    }

    /**
     * 校验参数
     * @param path
     * @param param
     */
    private void checkParam(String path, Map<String, String> param) throws IllegalArgumentException{
        if(StringUtils.isEmpty(path)){
            throw new IllegalArgumentException("路径不能为空");
        }
        if(Objects.isNull(param)){
            throw new IllegalArgumentException("param不能为null");
        }
    }

    /**
     * 发送GET请求
     * @param path
     * @param param
     * @return
     * @throws URISyntaxException
     * @throws IOException
     */
    protected String get(String path, Map<String, String> param) throws URISyntaxException, IOException {
        this.checkParam(path, param);
        this.completeParam(param);
        String requestBody = OkhttpClientUtils.get(this.buildUrl(path), param);
        return requestBody;
    }

    /**
     * 发送POST请求
     * @param path
     * @param param
     * @return
     * @throws URISyntaxException
     * @throws IOException
     */
    protected String postFormBody(String path, Map<String, String> param) throws URISyntaxException, IOException {
        this.checkParam(path, param);
        this.completeParam(param);
        String requestBody = OkhttpClientUtils.postFormBody(this.buildUrl(path), param);
        return requestBody;
    }

    private Map<String, String> objectToMap(Object param)  {
        Map<String, String> map = new HashMap<>();
        Gson gson = new Gson();
        String json = gson.toJson(param);
        JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject();
        Set<Map.Entry<String, JsonElement>> entrySet = jsonObject.entrySet();
        entrySet.forEach((entry) ->{
            String key = entry.getKey();
            JsonElement value = entry.getValue();
            value.getAsString();
            map.put(key, value.getAsString());
        });
        return map;

    }

    protected String postJsonBody(String path, Object paramRequestBody, Map<String, String> paramQueryString) throws URISyntaxException, IOException, ApiException {
        if(StringUtils.isEmpty(path)){
            throw new IllegalArgumentException("路径不能为空");
        }
        if(Objects.isNull(paramRequestBody)){
            throw new IllegalArgumentException("param不能为null");
        }
        if(Objects.isNull(paramQueryString)){
            throw new IllegalArgumentException("paramQueryString不能为null");
        }
        // 查询字符串中的参数
        Map<String, String> queryString = new HashMap<>();
        // 对象转map用于计算sign
        Map<String, String> paramMap = this.objectToMap(paramRequestBody);
        queryString.put(ParamConstant.APP_KEY, this.appKey);
        queryString.put(ParamConstant.FORMAT, FormatConstant.JSON);
        queryString.put(ParamConstant.TIMESTAMP, timestamp());
        queryString.putAll(paramQueryString);
        paramMap.putAll(queryString);
        String sign = SignUtils.sign(this.appKey, this.secretKey, paramMap);
        queryString.put(ParamConstant.SIGN, sign.toUpperCase());

        HttpUrl httpUrl = OkhttpClientUtils.buildUrlQueryString(this.buildUrl(path), queryString);
        return OkhttpClientUtils.postJsonBody(httpUrl.toString(), paramRequestBody);
    }
}
