package cn.pluss.platform.ryx.impl;

import cn.jiguang.common.utils.StringUtils;
import cn.pluss.platform.entity.RyxAccessModel;
import cn.pluss.platform.entity.RyxAccessResp;
import cn.pluss.platform.entity.RyxPayRequest;
import cn.pluss.platform.exception.MsgException;
import cn.pluss.platform.ryx.RyxService;
import cn.pluss.platform.util.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author DJH
 */
@Slf4j
@Service
public class RyxServiceImpl implements RyxService {

    @Resource
    private RestTemplate restTemplate;

    @Override
    public RyxAccessModel accessCombineEncrypt(Map<String, Object> paramMap, List<ByteArrayResource> files, String accessId, String pubKey) {
        RyxAccessModel ryxAccessModel = new RyxAccessModel();
        ryxAccessModel.setAccessId(accessId);
        // 接入方订单号
        ryxAccessModel.setAccessMerchId(UUID.randomUUID().toString());
        // 发起请求的时间
        ryxAccessModel.setReqTime(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()));

        // 保存AES密钥
        String ivParam = AESEncrypt.getIVParam(16);
        byte[] aesKey = null;
        try {
            aesKey = AESEncrypt.initKey();
            Map<String, String> keyMap = new HashMap<>();
            // aesKey使用Base64编码
            keyMap.put(AES_KEY, Base64.getEncoder().encodeToString(aesKey));
            keyMap.put(IV_PARAM, ivParam);
            RSAPublicKey publicKey = (RSAPublicKey) RSAUtils.getPublicKey(pubKey);
            String encryptKey = RSAUtils.encrypt(publicKey, JSON.toJSONString(keyMap));
            ryxAccessModel.setEncryptKey(encryptKey);
        } catch (Exception e) {
            e.printStackTrace();
            MsgException.throwException("瑞银信-AES密钥加密异常");
        }
        

        // 使用AES加密普通参数
        try {
            String info = AESEncrypt.encryptToString(JSON.toJSONString(paramMap).getBytes(StandardCharsets.UTF_8), aesKey, ivParam);
            ryxAccessModel.setInfo(info);
        } catch (Exception e) {
            e.printStackTrace();
            MsgException.throwException("瑞银信-请求参数加密异常");
        }

        // 参数加签
        Object[] sortedKeys = paramMap.keySet().toArray();
        Arrays.sort(sortedKeys);

        StringBuilder builder = new StringBuilder();
        builder.append("accessId=").append(ryxAccessModel.getAccessId())
                .append("&reqTime=").append(ryxAccessModel.getReqTime())
                .append("&ivParam=").append(ivParam);
        for (Object key : sortedKeys) {
            builder.append("&").append(key).append("=").append(paramMap.get(key));
        }

        log.debug("签名原串：{}", builder);
        // 客户端使用私钥签名
        try {
            RSAPrivateKey accessPrivateKey = (RSAPrivateKey) RSAUtils.getPrivateKey(RyxConfig.cooperatorPriKey);
            String sign = RSAUtils.signSHA256WithRSA(accessPrivateKey, builder.toString());
            ryxAccessModel.setSign(sign);
        } catch (Exception e) {
            e.printStackTrace();
            MsgException.throwException("瑞银信-签名异常");
        }

        // 组合文件信息
        if (files != null && !files.isEmpty()) {
            ryxAccessModel.setFiles(files);
        }

        return ryxAccessModel;
    }

    @Override
    public <T> T accessResultDecrypt(RyxAccessModel ryxAccessModel, Class<T> clazz) {
        log.debug("解密信息");
        if (ryxAccessModel == null || StringUtils.isEmpty(ryxAccessModel.getInfo())) {
            throw new MsgException("请求异常");
        }

        String info = ryxAccessModel.getInfo();
        String encryptKey = ryxAccessModel.getEncryptKey();
        String sign = ryxAccessModel.getSign();
        String reqTime = ryxAccessModel.getReqTime();

        Map<String, Object> aesKeyMap = null;
        try {
            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) RSAUtils.getPrivateKey(RyxConfig.cooperatorPriKey);
            // 使用客户端RSA密钥解密 AES密钥
            String aesKeyInfo = RSAUtils.decrypt(rsaPrivateKey, encryptKey);
            aesKeyMap = JSON.parseObject(aesKeyInfo);
        } catch (Exception e) {
            e.printStackTrace();
            MsgException.throwException("瑞银信-请求结果密钥解密异常");
        }

        MsgException.checkNull(aesKeyMap, "瑞银信-请求结果密钥解密异常");
        
        // AES解密服务器返回的数据
        String data = null;
        String ivParam = null;
        try {
            Base64.Decoder decoder = Base64.getDecoder();
            byte[] bytesInfo = decoder.decode(info);
            byte[] key = decoder.decode(aesKeyMap.get(AES_KEY).toString());
            ivParam = aesKeyMap.get(IV_PARAM).toString();
            byte[] decryptData = AESEncrypt.decrypt(bytesInfo, key, ivParam);
            data = new String(decryptData, StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
            MsgException.throwException("瑞银信-请求结果密钥解密异常");
        }

        log.info(">>>>>>>>返回报文：\n"+data);

        T result = JSON.parseObject(data, clazz);

        StringBuilder builder = new StringBuilder();
        builder.append("accessId=").append(ryxAccessModel.getAccessId()).append("&reqTime=").append(reqTime)
                .append("&ivParam=").append(ivParam).append("&result=").append(data);

        try {
            RSAPublicKey rsaPublicKey = (RSAPublicKey) RSAUtils.getPublicKey(RyxConfig.accessPubKey);
            // 使用服务端的公钥验证签名
            boolean flag = RSAUtils.checkSignSHA256WithRSA(rsaPublicKey, builder.toString(), sign);
            log.debug(flag ?"验签成功":"验签失败");
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

    @Override
    public RyxAccessResp reqCommon(Map<String, Object> paramMap, List<ByteArrayResource> files, String accessId, String pubKey, String url) {
        // 组装加密请求参数
        final RyxAccessModel ryxAccessModel = accessCombineEncrypt(paramMap, files, accessId, pubKey);
        // 加密的参数转化为通用Map
        MultiValueMap<String, Object> map = RyxAccessModel.toMap(ryxAccessModel);
        if (!url.equals(RyxConfig.merchantIncome)) {
            map.remove("accessMerchId");
        }
        HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(map);
        // 发起请求
        RyxAccessModel ryxResult = restTemplate.postForObject(url, request, RyxAccessModel.class);
        // 返回参数校验
        MsgException.checkNull(ryxResult, "未知请求结果");
        // 解密并返回请求结果
        return accessResultDecrypt(ryxResult, RyxAccessResp.class);
    }

    @Override
    public RyxPayRequest payParamsEncrypt(JSONObject params,String payWay) {

        RyxPayRequest request = new RyxPayRequest();
        request.setCooperator(RyxConfig.payCooperator);
        try {
            byte[] plainBytes = params.toJSONString().getBytes(StandardCharsets.UTF_8);

            RSAPrivateKey prvKey = (RSAPrivateKey)RSAUtils.getPrivateKey(RyxConfig.cooperatorPriKey);
            RSAPublicKey publicKey = (RSAPublicKey) RSAUtils.getPublicKey(RyxConfig.smzfPubKey);

            String key = AESEncrypt.getIVParam(16);
            byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);

            String encryptData = AESEncrypt.encryptEcbToString(plainBytes, keyBytes,null);
            String signData = RSAUtils.sign(prvKey, params.toJSONString());
            String encrtptKey = RSAUtils.encrypt(publicKey, key);

            request.setEncryptData(encryptData);
            request.setSignData(signData);
            request.setEncrtptKey(encrtptKey);
            request.setTranCode(payWay);
            request.setCallBack(ParametersUtil.domain + "/wap/notify/ryxCallBack");
            request.setReqMsgId(params.getString("reqMsgId"));
        } catch (Exception e) {
            e.printStackTrace();
            MsgException.throwException("瑞银信-支付请求参数加密异常");
        }
        return request;
    }

    /**
     * 解析返回参数
     * @param respones
     * @return
     */
    @Override
    public JSONObject analysisPayParamsEncrypt(String respones) {

        JSONObject resp = JSONObject.parseObject(respones);

        String resEncryptData = resp.getString("encryptData");

        String resEncryptKey = resp.getString("encryptKey");

        if(resEncryptData == null || resEncryptKey == null){
            String respMsg = StringUtil.isEmpty(resp.getString("respMsg")) ? "请求异常" : resp.getString("respMsg");
            MsgException.throwException(respMsg);
        }
        try {

            RSAPrivateKey prvKey = (RSAPrivateKey)RSAUtils.getPrivateKey(RyxConfig.cooperatorPriKey);

            byte[] keyBytes = Base64.getDecoder().decode(resEncryptKey.getBytes(StandardCharsets.UTF_8));

            byte[] merchantAESKeyBytes = RSAUtils.decryptEcb(keyBytes,prvKey,2048,11);

            byte[] dataBytes = Base64.getDecoder().decode(resEncryptData.getBytes(StandardCharsets.UTF_8));

            String resXml = AESEncrypt.decryptEcbToString(dataBytes, merchantAESKeyBytes, null);

            log.info("=============>【瑞银信】返回参数解析结果:{}<================",resXml);

            return JSONObject.parseObject(resXml);
        } catch (Exception e) {
            e.printStackTrace();
            MsgException.throwException("【瑞银信】返回参数解析异常");
        }
        return null;
    }

    /**
     * 瑞银信封装通用支付请求参数
     * @param reqData
     * @return
     */
    @Override
    public JSONObject analysisCommonPayParams(String url,String payWay,JSONObject reqData) {
        JSONObject params = new JSONObject(10);
        params.put("version","1.0.0");
        params.put("msgType","01");
        params.put("reqDate", DateUtils.formatDateDefault(new Date(),"yyyyMMddHHmmss"));
        params.put("reqMsgId", reqData.getString("reqMsgId") != null ? reqData.getString("reqMsgId") : StringUtil.genRandomNum(16));
        reqData.remove("reqMsgId");
        params.put("data",reqData);

        log.info("==============>【瑞银信】接口请求，请求参数：{}<==================",JSONObject.toJSONString(params));

        RyxPayRequest request = payParamsEncrypt(params, payWay);

        MultiValueMap<String, Object> toParamMap = request.toMap();

        HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(toParamMap);

        log.info("==========>【瑞银信】请求参数：{}<============", JSON.toJSONString(entity));

        byte[] result = restTemplate.postForObject(url, entity, byte[].class);

        String respones = new String(result, StandardCharsets.UTF_8);

        log.info("==========>【瑞银信】响应参数：{}<============", respones);

        MsgException.checkNull(respones, "==========>【瑞银信】未知请求结果<============");

        JSONObject object = analysisPayParamsEncrypt(respones);

        MsgException.checkNull(object, "==============>【瑞银信】请求结果解析异常<============");

        return object;
    }

    @Override
    public JSONObject downloadFtpFile(String date) {

        JSONObject object = new JSONObject(2);
        //瑞银信交易上送的机构号
        String orgId = RyxConfig.payCooperator;
        String nowDate = DateUtils.toString(DateUtils.add(Calendar.DATE,new Date(),-1),"yyyyMMdd");
        if(StringUtil.isNotEmpty(date)){
            nowDate = date;
        }
        String fileName = filePrefix + orgId + "_" +nowDate;
        String uploadPath =  ParametersUtil.upload_save_path + "/ftp";
        Properties prop = System.getProperties();
        String os = prop.getProperty("os.name");
        if (!(os.startsWith("win") || os.startsWith("Win"))) {
            try {
                Runtime.getRuntime().exec("chmod -R 777 /home/syb/resources/");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else{
            uploadPath = "E:/crystal/ftp/file/ftp";
        }
        try {
            File file = new File(uploadPath+"/");
            if(!file.exists()){
                log.info("不包含文件目录:{}，需要创建文件目录",uploadPath);
                file.mkdirs();
                log.info("创建文件夹:{}成功",uploadPath);
            }

            log.info("【瑞银信】文件下载参数,下载文件前缀:{},日期:{},文件保存路径:{},hostName:{},port:{},userName:{},password:{}",prefix,nowDate,uploadPath,hostName,port,userName,password);
            boolean flag = FtpUtils.downloadFiles(prefix, nowDate, uploadPath, hostName, port, userName, password);
            log.info("文件下载状态:{}",flag);
            if(!flag){
                log.info("================>【瑞银信】ftp交易数据文件下载失败<==================");
                return null;
            }
            log.info("【瑞银信】解压文件参数，解压之前文件名：{}，文件路径：{}，解压成功之后的文件名：{}",fileName + suffix,uploadPath,fileName + unZipSuffix);
            //解压文件
            FtpUtils.extractFile(fileName + suffix,uploadPath,fileName + unZipSuffix);
            //文件路径
            String uFilePath = uploadPath + File.separator + fileName + unZipSuffix;
            object.put("uploadPath",uploadPath);
            object.put("filePath",uFilePath);
            log.info("================>【瑞银信】返回的文件路径参数:{}<==================",object);
            return object;
        } catch (IOException e) {
            e.printStackTrace();
            log.info("===================>解压ftp文件异常，异常信息:{}<=================",e.getMessage());
            MsgException.throwException(e.getMessage());
        }
        return null;
    }
}
