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.fasterxml.jackson.databind.ObjectMapper;
import com.friday.custody.dto.EbscnAppSecretDTO;
import com.friday.custody.dto.TenantEscrowAuthorizationInfoDto;
import com.friday.custody.entity.InvokeRecordEntity;
import com.friday.custody.service.basicService.InvokeRecordService;
import com.friday.custody.service.basicService.impl.PullDataFailureLogServiceImpl;
import com.friday.custody.service.trustService.ebscn.common.EbscnConstants;
import com.friday.custody.utils.PullDataFailureUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static com.friday.custody.service.trustService.ebscn.common.EbscnConstants.SUCCESS_CODE;
import static com.friday.custody.service.trustService.ebscn.utils.OpenApiHelper.decryptBizData;
import static com.friday.custody.service.trustService.ebscn.utils.OpenApiHelper.httpPost;
import static com.friday.custody.service.trustService.ebscn.utils.SM2Utils.encryptBizData;


@Slf4j
@Service
public class EbscnRemoteUtil {
    @Autowired
    private OpenApiHelper openApiHelper;

    @Autowired
    private InvokeRecordService invokeRecordService;

    @Autowired
    private PullDataFailureLogServiceImpl pullDataFailureLogService;

    @Value("${custody.remote.url.ebscn.ebscnUrlPrefix}")
    private String serverUrl;

    private ObjectMapper objectMapper = new ObjectMapper();

    public Map<String, Object> sendMessage(Map<String, Object> params,
                                           TenantEscrowAuthorizationInfoDto tenant, boolean forceNew){
        //添加获取总数参数
        params.put("needcountflag", "1");
        log.info("请求参数{}, 配置信息：{}",JSON.toJSONString(params, true), JSON.toJSONString(tenant));
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            log.error("光大证券的睡眠被打断了。。。。", e);
            Thread.currentThread().interrupt();
        }
        //获取账密信息
        String tenantAppSecret = tenant.getAppSecret();
        EbscnAppSecretDTO ebscnAppSecretDTO = new EbscnAppSecretDTO();
        try {
            ebscnAppSecretDTO = objectMapper.readValue(tenantAppSecret, EbscnAppSecretDTO.class);
        } catch (IOException e) {
            log.error("解析光大证券账密失败!", e);
        }
        String userName = ebscnAppSecretDTO.getUsername();
        String password = ebscnAppSecretDTO.getPassword();
        String clientId = ebscnAppSecretDTO.getAppKey();
        String clientSercet = ebscnAppSecretDTO.getAppSecret();
        String sm2CertDir = tenant.getFilePath() + ebscnAppSecretDTO.getCerFilePath();
        log.info("证书入参：sm2CertDir：{}， clientSercet：{}", sm2CertDir, clientSercet);
        SM2PrivateKey sm2PrivateKey = SM2Utils.getSM2PrivateKeyFromCert(sm2CertDir
//                + File.separator + clientId + EbscnConstants.CERT_SUFFIX
                , clientSercet);
        log.info("Ebscn:从证书文件中获取私钥：" + (sm2PrivateKey == null ? "失败" : "成功"));

        Map<String, Object> result = new HashMap<>();
        String errorMsg = null;

            // 请求头
        String accessToken = null;
        try {
            accessToken = OpenApiHelper.getAccessToken(serverUrl, userName, password, forceNew, clientId, sm2PrivateKey, clientSercet);
        } catch (Exception e) {
            log.error("Ebscn get accessToken Fail!", e);
        }
        Map<String, String> headers = new HashMap<>();
        try {
            headers.put("Content-Type", "application/x-www-form-urlencoded");
            headers.put("Access-Token", accessToken);
            String resultStr = httpPost(serverUrl, headers, SM2Utils.encryptBizData(params, clientSercet, sm2PrivateKey));
            // 请求结果
            result = decryptBizData(JSONObject.parseObject(resultStr), clientSercet, sm2PrivateKey);
            log.info("Ebscn:请求头{}，请求参数{}，请求结果{}", headers, params, new JSONObject(result));

            // 如果token失效，则强制重新获取
            if (!forceNew && EbscnConstants.AUTH_TOKEN_INVALID_CODE.equals(result.get("status"))) {
//                return OpenApiHelper.request(params, userName, password, true, clientId,  clientSercet, sm2PrivateKey);
                return sendMessage(params, tenant, true);
            }
            if (SUCCESS_CODE.equals(result.get("status"))){
                //保存调用记录
                saveInvokeRecord(tenant, tenant.getTrustNo(), params.get("service_name").toString(), "", params, result);
                return result;
            }
        }catch (Exception e){
            log.error("Ebscn调用接口失败！",e);
            errorMsg = e.getMessage();
        }
        if (StringUtils.isEmpty(errorMsg)){
            errorMsg = JSONObject.toJSONString(result);
        }
        log.error("ebscn调用上游接口失败!url:{},admin:{}", params.get("service_name"), tenant.getTenantName());
        pullDataFailureLogService.saveErrorData(tenant.getTenantName(), PullDataFailureUtil.createErrorMap(tenant.getTrustNo(), params.get("service_name").toString(),JSON.toJSONString(headers), JSONObject.toJSONString(params), errorMsg));
        return result;
    }

    public void saveInvokeRecord(TenantEscrowAuthorizationInfoDto tenant, String trustNo, String invokeType, String headers, Map<String,Object> params, Map<String,Object> invokeResult) {
        Date now = new Date();
        InvokeRecordEntity invokeRecordEntity = InvokeRecordEntity.builder()
                .trustNo(trustNo)
                .admin(tenant.getTenantName())
                .invokeType(invokeType)
                .reqHeader(JSON.toJSONString(headers))
                .reqParam(JSON.toJSONString(params))
                .invokeTime(now)
                .invokeResult(JSON.toJSONString(invokeResult))
                .queryKey(tenant.getTenantName() + "::" + trustNo + "::" + invokeType)
                .createTime(now)
                .updateTime(now)
                .build();
        invokeRecordService.save(invokeRecordEntity);
    }
}
