package com.friday.custody.service.trustService.ebscn.utils;

import cfca.sadk.algorithm.sm2.SM2PrivateKey;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.friday.custody.service.trustService.ebscn.common.EbscnConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;

import java.io.FileNotFoundException;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;



/**
 * <pre>
 * 摘  要：OPENAPI调用帮助/示例
 * TODO 注意：代码中TODO标记的地方自行调整 ################
 *
 * 注意： 高版本java可能出现"No appropriate protocol"错误
 * 此时修改jdk/jre/lib/security/java.security文件中的jdk.tls.disabledAlgorithms，去掉TLSv1
 *
 * 作  者：吴丰辉(wufh@ebscn.com)
 * 时  间：2022/01/07 21:45
 *
 * Copyright © 版权2022 光大证券股份有限公司
 * </pre>
 **/
@Slf4j
@Service
public class OpenApiHelper {



    /**
     * 用户token TODO 实际场景中可以存到redis之类的缓存或者数据库中
     */
    private static final Map<String, Map<String, Object>> USER_TOKEN = new HashMap<>();
    static {
        Map<String, Object> env = new HashMap<>(6);
//        SM2_CERT_DIR = ClassLoader.getSystemResource("ebscn/sm2cert/EBSCN-tuuFwMXvjgLkRHDv.ebscn.cer").getPath();
//        env.put("SM2_CERT_DIR",SM2_CERT_DIR);
        setEnv(env);
    }


    /**
     * 设置环境变量
     * @param env 环境变量
     */
    public static void setEnv(Map<String, Object> env) {

        // 从证书文件获取私钥
//        if (env.containsKey("SM2_CERT_DIR")) {
//            SM2_PRIVATE_KEY = SM2Utils.getSM2PrivateKeyFromCert(
//                    SM2_CERT_DIR
//                            //+ File.separator + EbscnConstants.CLIENT_ID + EbscnConstants.CERT_SUFFIX
//                    , EbscnConstants.CLIENT_SECRET);
//            System.out.println("从证书文件中获取私钥：" + (SM2_PRIVATE_KEY == null ? "失败" : "成功"));
//        } else if (env.containsKey("SM2_PRIVATE_KEY")) {
//            // 直接获取私钥
//            SM2_PRIVATE_KEY =  (SM2PrivateKey) env.get("SM2_PRIVATE_KEY");
//        }
        // 清空令牌
        USER_TOKEN.clear();
    }

    /**
     * 加密字符串
     *
     * @param srcStr 原始字符串
     * @return 加密后的字符串
     */
    public static String encryptStr(SM2PrivateKey sm2PrivateKey, String srcStr) throws Exception {
        return SM2Utils.encrypt4SM2(srcStr, sm2PrivateKey.getSM2PublicKey());
    }

    /**
     * 加密业务数据
     *
     * @param bizData 业务数据
     * @return 加密后的业务数据
     */
    public static Map<String, Object> encryptBizData(Map<String, Object> bizData, String clientId, SM2PrivateKey sm2PrivateKey) throws Exception {
        return SM2Utils.encryptBizData(bizData, clientId, sm2PrivateKey);
    }

    /**
     * 业务数据SM2签名验证及解密
     * @param bizData 业务数据
     * @param clientSercet
     * @param sm2PrivateKey
     * @return
     */
    public static Map<String, Object> decryptBizData(Map<String, Object> bizData, String clientSercet, SM2PrivateKey sm2PrivateKey) {
        return SM2Utils.verifyAndDecryptBizData(bizData, clientSercet, sm2PrivateKey);
    }

    /**
     * 获取token
     *
     * @param userName 用户名
     * @param password 密码
     * @param b
     * @return token
     * @throws Exception 异常
     */
    public static String getAccessToken(String userName, String password, boolean b) throws Exception {
        return getAccessToken(userName, password, true);
    }

    /**
     * 获取token
     *
     * @param userName 用户名
     * @param password 密码
     * @param forceNew 是否强制刷新(默认为false，当请求结果为令牌失效时为true)
     * @return token
     * @throws Exception 异常
     */
    public static String getAccessToken(String serverUrl, String userName, String password, boolean forceNew, String clientId, SM2PrivateKey sm2PrivateKey, String clientSercet) throws Exception {
        // 公共请求头
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/x-www-form-urlencoded");
        headers.put("Access-Token", clientId);
        // 业务参数
        Map<String, Object> bizData = new HashMap<>();
        // 请求结果
        String resultStr, accessToken, refreshToken, expiresIn;
        Date bgnDate = new Date(), expiresInDate;

        // 先尝试从缓存中取token
        Map<String, Object> tokenMap = USER_TOKEN.get(userName);
        if (null == tokenMap) {
            // 没有时创建
            tokenMap = new HashMap<>();
            USER_TOKEN.put(userName, tokenMap);
        } else if (forceNew) {
            // 强制刷新令牌时，清除缓存信息
            tokenMap.clear();
        } else {
            // 有的话先检查access_token是不是存在
            accessToken = (String) tokenMap.get("access_token");
            if (StringUtils.isNotBlank(accessToken)) {
                // 如果有有效期的话检查是否过期
                expiresInDate = (Date) tokenMap.get("expires_in");
                if (null != expiresInDate && expiresInDate.before(new Date())) {
                    // 过期需要刷新
                    refreshToken = (String) tokenMap.get("refresh_token");
                    System.out.println("\n\t---- 刷新令牌 RefreshToken");
                    bizData.put("service_name", "RefreshToken");
                    bizData.put("refresh_token", refreshToken);
                    log.info("请求参数{}",JSON.toJSONString(bizData, true).replace("\n", "\n\t"));
//                    System.out.println("\t请求参数：" + JSON.toJSONString(bizData, true).replace("\n", "\n\t"));

                    resultStr = httpPost(serverUrl, headers, encryptBizData(bizData, clientSercet, sm2PrivateKey));

                    bizData = decryptBizData(JSONObject.parseObject(resultStr), clientSercet, sm2PrivateKey);
                    log.info("请求结果{}",JSON.toJSONString(bizData, true).replace("\n", "\n\t"));
//                    System.out.println("\t请求结果:" + JSON.toJSONString(bizData, true).replace("\n", "\n\t"));

                    // 刷新到则更新，否则按照未失效处理
                    if (bizData.containsKey("access_token")) {
                        accessToken = (String) bizData.get("access_token");
                        refreshToken = (String) bizData.get("refresh_token");
                        expiresIn = (String) bizData.get("expires_in");
                        // 缓存刷新token和过期时间
                        tokenMap.put("access_token", accessToken);
                        tokenMap.put("refresh_token", refreshToken);
                        tokenMap.put("expires_in", DateUtils.addMinutes(bgnDate, Integer.parseInt(expiresIn)));
                    }
                }
                System.out.println();
                // 过期后刷新后返回;未过期或者没有过期时间，则直接返回token
                return accessToken;
            }
        }


        // 其它情况通过授权码获取token
        String authCode;
        System.out.println("\n\t---- 获取授权码 GetApiAuthCode");
        bizData.put("service_name", "GetApiAuthCode");
        bizData.put("username", userName);
        bizData.put("password", encryptStr(sm2PrivateKey,password));
        bizData.put("redirect_uri", "-");
        bizData.put("response_type", "code");
        System.out.println("\t请求参数：" + JSON.toJSONString(bizData, true).replace("\n", "\n\t"));

        resultStr = httpPost(serverUrl, headers, encryptBizData(bizData, clientSercet, sm2PrivateKey));
        bizData = decryptBizData(JSONObject.parseObject(resultStr), clientSercet, sm2PrivateKey);
        System.out.println("\t请求结果:" + JSON.toJSONString(bizData, true).replace("\n", "\n\t"));
        authCode = (String) bizData.get("code");
        accessToken = (String) bizData.get("access_token");


        // 令牌失效时重新获取令牌
        if (StringUtils.isNotBlank(authCode) && StringUtils.isBlank(accessToken)) {
            bizData.clear();
            System.out.println("\n\t---- 获取令牌 GetApiToken");
            bizData.put("service_name", "GetApiToken");
            bizData.put("client_id", clientId);
            bizData.put("client_secret", encryptStr(sm2PrivateKey, clientSercet ));
            bizData.put("code", authCode);
            bizData.put("grant_type", "authorization_code");
            bizData.put("redirect_uri", "-");
            System.out.println("\t请求参数：" + JSON.toJSONString(bizData, true).replace("\n", "\n\t"));

            resultStr = httpPost(serverUrl, headers, encryptBizData(bizData, clientSercet, sm2PrivateKey));

            bizData = decryptBizData(JSONObject.parseObject(resultStr), clientSercet, sm2PrivateKey);
            System.out.println("\t请求结果:" + JSON.toJSONString(bizData, true).replace("\n", "\n\t"));
            accessToken = (String) bizData.get("access_token");
            refreshToken = (String) bizData.get("refresh_token");
            expiresIn = (String) bizData.get("expires_in");
            // 缓存刷新token和过期时间
            tokenMap.put("refresh_token", refreshToken);
            tokenMap.put("expires_in", DateUtils.addMinutes(bgnDate, Integer.parseInt(expiresIn)));
        }

        // 缓存token
        tokenMap.put("access_token", accessToken);

        System.out.println();
        // 返回token
        return accessToken;
    }

    /**
     * 请求数据(使用默认账号密码)
     *
     * @param params 请求参数
     * @return token
     * @throws Exception 异常
     */
    public static Map<String, Object> request(Map<String, Object> params) throws Exception {
        return request(params, EbscnConstants.DEFAULT_USER_NAME, EbscnConstants.DEFAULT_PASSWORD, false);
    }

    private static Map<String, Object> request(Map<String, Object> params, String defaultUserName, String defaultPassword, boolean b) {
        return null;
    }

    /**
     * 请求数据
     *
     * @param params   请求参数
     * @param userName 用户名
     * @param password 密码
     * @return token
     * @throws Exception 异常
     */
    public static Map<String, Object> request(Map<String, Object> params,
                                              String userName, String password) throws Exception {
        return request(params, userName, password, false);
    }

    /**
     * 请求数据
     *
     * @param params   请求参数
     * @param userName 用户名
     * @param password 密码
     * @param forceNew 是否强制刷新(默认为false，当请求结果为令牌失效时为true)
     * @return token
     * @throws Exception 异常
     */
    public static Map<String, Object> request(String serverUrl, Map<String, Object> params,
                                              String userName, String password, boolean forceNew, String clientId, String clientSercet, SM2PrivateKey sm2PrivateKey) throws Exception {
        System.out.println("请求参数：" + JSON.toJSONString(params, true));
        // 请求头
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/x-www-form-urlencoded");
        headers.put("Access-Token", getAccessToken(serverUrl, userName, password, forceNew, clientId, sm2PrivateKey, clientSercet));

        String resultStr = httpPost(serverUrl, headers, encryptBizData(params, clientSercet, sm2PrivateKey));

        // 请求结果
        Map<String, Object> result = decryptBizData(JSONObject.parseObject(resultStr), clientSercet, sm2PrivateKey);

        // 如果token失效，则强制重新获取
        if (!forceNew && EbscnConstants.AUTH_TOKEN_INVALID_CODE.equals(result.get("status"))) {
            return request(serverUrl, params, userName, password, true, clientId, clientSercet, sm2PrivateKey);
        }

        System.out.println("请求结果:" + JSON.toJSONString(result, true));
        return result;
    }

    /**
     * POST请求接口获取结构
     * <p>
     * TODO #################################################################################################################################################
     * TODO POST请求方法可根据自己需要实现
     * TODO #################################################################################################################################################
     *
     * @param url     目标地址
     * @param headers 请求头
     * @param params  参数
     * @return 返回信息
     */
    public static String httpPost(String url, Map<String, String> headers, Map<String, Object> params) {
        return HttpUtils.post(url, headers, params);
    }



    /**
     * 测试数据
     */

    public static void main(String[] args) throws Exception {
        URL resource = null;
        try {
            resource = ResourceUtils.getURL("ebscn/sm2cert/EBSCN-tuuFwMXvjgLkRHDv.ebscn.cer");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println(resource);
        // 业务参数
//        Map<String, Object> bizData = new HashMap<>();
//        bizData.put("","");
//
//        // 查询投资人信息
//        bizData.clear();
//        System.out.println("\n################## 查询投资人信息 P2000006");
//        bizData.put("service_name ", "P2000006");
//        bizData.put("version", "EBS-PUB-V1.0");
//        bizData.put("needcountflag", "1");
//        request(bizData);
//        // 验证令牌
//        bizData.clear();
//        System.out.println("\n################## 验证令牌 ValidityToken");
//        bizData.put("service_name", "ValidityToken");
//        request(bizData);

//        testP2000094();

//        testP2000007();
//        testP2000061();
//        testP2000008();
//        testP2000060();
//        testP2000009();
//        testP3900021();
//        testP3900022();
        testP2000279();
    }


    private static void testP2000051() throws Exception {
        Map<String,Object> params = new HashMap<>();
        params.put("service_name", "P2000051");
        params.put("version", "EBS-BIZ-V1.0");
        params.put("limit", "500");
        params.put("pageIndex", "1");
        params.put("start_occurdate","20220526");
        params.put("end_occurdate","20220609");
//        request(params);
    }
    private static void testP2000279() throws Exception {
        Map<String,Object> params = new HashMap<>();
        params.put("service_name", "P2000279");
        params.put("version", "EBS-BIZ-V1.0");
        params.put("limit", "500");
        params.put("pageIndex", "1");
        params.put("start_ldate","20220526");
        params.put("end_ldate","20220609");
//        request(params);
    }
    private static void testP3900022() throws Exception {
        Map<String,Object> params = new HashMap<>();
        params.put("service_name", "P3900022");
        params.put("version", "EBS-BIZ-V1.0");
        params.put("limit", "500");
        params.put("pageIndex", "1");
        params.put("start_tradetime","20220526");
        params.put("end_tradetime","20220609");
        params.put("isToYqzl","1");
//        request(params);
    }
    private static void testP3900021() throws Exception {
        Map<String,Object> params = new HashMap<>();
        params.put("service_name", "P3900021");
        params.put("version", "EBS-BIZ-V1.0");
        params.put("limit", "500");
        params.put("pageIndex", "1");
        params.put("isToYqzl","1");

//        request(params);
    }


    private static void testP2000009() throws Exception {
        Map<String,Object> params = new HashMap<>();
        params.put("service_name", "P2000009");
        params.put("version", "EBS-BIZ-V1.0");
        params.put("limit", "500");
        params.put("pageIndex", "1");
        params.put("start_date","20210526");
        params.put("end_date","20210609");

//        request(params);
    }

    private static void testP2000060() throws Exception {
        Map<String,Object> params = new HashMap<>();
        params.put("service_name", "P2000060");
        params.put("version", "EBS-BIZ-V1.0");
        params.put("limit", "500");
        params.put("pageIndex", "1");
        params.put("start_date","20210526");
        params.put("end_date","20210609");

//        request(params);
    }

    private static void testP2000008() throws Exception {
        Map<String,Object> params = new HashMap<>();
        params.put("service_name", "P2000008");
        params.put("version", "EBS-BIZ-V1.0");
        params.put("limit", "500");
        params.put("pageIndex", "1");

//        request(params);
    }

    private static void testP2000061() throws Exception {
        Map<String,Object> params = new HashMap<>();
        params.put("service_name", "P2000061");
        params.put("version", "EBS-BIZ-V1.0");
        params.put("limit", "500");
        params.put("pageIndex", "1");

        params.put("start_date","20220526");
        params.put("end_date","20220609");
        params.put("sfqr","1");
        params.put("sfqr1","1");
//        request(params);
    }

    private static void testP2000007() throws Exception {
        Map<String,Object> params = new HashMap<>();
        params.put("service_name", "P2000007");
        params.put("version", "EBS-BIZ-V1.0");
        params.put("limit", "500");
        params.put("pageIndex", "1");

//        request(params);
    }

    private static void testP2000094() throws Exception {
        Map<String,Object> params = new HashMap<>();
        params.put("service_name", "P2000094");
        params.put("version", "EBS-BIZ-V1.0");
        params.put("limit", "500");
        params.put("pageIndex", "1");


//        request(params);
    }






}