package com.fastbee.data.controller.pay;

import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONException;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.json.ObjectMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dtflys.forest.annotation.Success;
import com.fastbee.common.core.controller.BaseController;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.utils.pay.AesUtil;
import com.fastbee.common.utils.pay.RSAUtil;
import com.fastbee.rechargecard.domain.NgMerchants;
import com.fastbee.rechargecard.domain.dto.WeChatRecharge;
import com.fastbee.rechargecard.domain.dto.WeChatRechargeBacktracking;
import com.fastbee.rechargecard.mapper.NgIrrigationControllersMapper;
import com.fastbee.rechargecard.mapper.NgMerchantsMapper;
import com.fastbee.rechargecard.service.INgUserRechargeRecordsService;
import com.fastbee.rechargecard.service.IUserConsumptionDetailsService;
import com.fastbee.rechargecard.service.IUserRechargeCardsService;
import com.fastbee.rechargecard.service.IUserWechatPayService;
import com.fastbee.system.domain.SysRoleDept;
import com.fastbee.system.mapper.SysDeptMapper;
import com.fastbee.system.mapper.SysRoleDeptMapper;
import com.fasterxml.jackson.databind.JsonNode;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.*;

import com.fastbee.common.utils.pay.wxPayConfig;

import static cn.hutool.core.util.XmlUtil.xmlToMap;
import static com.fastbee.common.constant.Constants.LANGUAGE;
import static com.fastbee.common.utils.pay.RSAUtil.verifySignature;
import static com.fastbee.common.utils.pay.RSAUtil.verifySignatureByCertificate;
import static com.fastbee.rechargecard.service.impl.UserWechatPayServiceImpl.*;

/**
 * @author mijiupro
 */
@Api(tags = "支付模块")
@Slf4j
@RestController
@RequestMapping("/pay")
public class WeChatPayController extends BaseController {


    @Autowired
    private INgUserRechargeRecordsService ngUserRechargeRecordsService;
    @Autowired
    private IUserRechargeCardsService userRechargeCardsService;
    @Autowired
    private IUserConsumptionDetailsService userConsumptionDetailsService;
    @Autowired
    private IUserWechatPayService userWechatPayService;

    @Autowired
    private NgMerchantsMapper ngMerchantsMapper;
    /**
     * 获取平台证书
     * @return
     */
    @ApiOperation("获取平台证书")
    @GetMapping("/getPlatformCertificate")
    public AjaxResult getPlatformCertificate(@RequestBody String mchId,@RequestBody String privateKey,@RequestBody String serial_no,@RequestBody String apiV3Key)
    {
        return success(userWechatPayService.getPlatformCertificat(mchId,privateKey,serial_no,apiV3Key));
    }

    /**
     * 获取openId
     * @param code
     * @return
     */
    @ApiOperation("获取openId")
    @PostMapping("/getOpenId")
    public Map<String,Object> getOpenId(@RequestBody String code)
    {
        if(StringUtils.isBlank(code)){
            return AjaxResult.error("code不能为空！");
        }
        Map<String ,Object> result=userWechatPayService.GetOpenId(code);
        Map<String,Object> returnParams=new HashMap<>();
        if(result.get("openid")==null && result.get("errcode")!=null)
        {
            if(result.get("errcode").equals(40163))
            {
                returnParams.put("code",401);
                returnParams.put("message","验证码已经被使用，请重新获取");
                return returnParams;
                //return warn("验证码已经被使用，请重新获取");
            }else if(result.get("errcode").equals(40029))
            {
                returnParams.put("code",403);
                returnParams.put("message","验证码已失效，请重新获取");
                return returnParams;
                //return warn("验证码已失效，请重新获取");
            }
        }
        if(result.get("openid")==null){
            return error("获取openid失败");
        }

        return success(result.get("openid").toString());
    }

    /**
     * 微信支付生成订单
     * @return 登录结果
     */
    @ApiOperation("生成订单")
    @PostMapping("/order")
    public AjaxResult BuildOrder(@RequestBody WeChatRecharge recharge) throws Exception {

        System.out.println("生成订单");
        String out_trade_no=UUID.randomUUID().toString().replace("-", "");
        while(ngUserRechargeRecordsService.SelectRechargeRecodeByRechargeCode(out_trade_no) !=null)
        {
            out_trade_no=UUID.randomUUID().toString().replace("-", "");
        }
        System.err.println("随机字符串"+out_trade_no);
        recharge.setRechargeCode(out_trade_no);
        Map<String, String> result = userWechatPayService.CreateOrder(recharge); // 生成订单
        if (result==null){
            return error("未查到相应的微信支付数据");
        }
        System.err.println("创建订单完成：" +result);
        //打印出返回前端的所有参数
        // 获取键的集合
        Set<String> keySet = result.keySet();
        // 遍历键集合
        for (String key : keySet) {
            System.out.println(key + ": " + result.get(key));
        }

        if(result.isEmpty())
        {
            System.out.println("微信支付订单生成失败");
            return error("微信支付订单生成失败");
        }
        System.out.println("微信支付订单生成成功");

        //订单生成成功
        int flag=ngUserRechargeRecordsService.insertNgUserRechargeRecordsWeChat(recharge);//插入用户充值记录表
        //flag=userConsumptionDetailsService.insertUserConsumptionDetailsWechat(recharge);//插入用户消费明细表
        if(flag==1)
        {
            return success(result);
        }
        return error("系统内部订单生成失败");
    }

    @PostMapping("/getresult")
    @ApiOperation("支付通知结果回溯")
    @ResponseBody
    public ResponseEntity<?> getResult(@RequestBody JSONObject jsonObject, HttpServletRequest request) throws Exception {//WeChatRechargeBacktracking backtracking

        System.out.println("微信回调开始---------");
        String wechatpaySignature = request.getHeader("Wechatpay-Signature");//验签的签名值
        String wechatpaySerial = request.getHeader("Wechatpay-Serial");//序列号
        String wechatpayTimestamp = request.getHeader("Wechatpay-Timestamp");//时间戳
        String wechatpayNonce = request.getHeader("Wechatpay-Nonce");//随机字符串
//        System.err.println("wechatpaySignature："+wechatpaySignature);
//        System.err.println("wechatpaySerial："+wechatpaySerial);
//        System.err.println("wechatpayTimestamp："+wechatpayTimestamp);
//        System.err.println("wechatpayNonce"+wechatpayNonce);
        String json=jsonObject.toString();
//        System.out.println("微信回调报文：{"+json+"}");
        // 构建应答报文
        Map<String, String> responseBody = new HashMap<>();
        //解密jsonObject对象
        String associated_data= (String) JSONUtil.getByPath(JSONUtil.parse(json),"resource.associated_data");//加密数据
        String ciphertext=(String) JSONUtil.getByPath(JSONUtil.parse(json),"resource.ciphertext");//数据密文
        System.out.println("ciphertext:"+ciphertext);
        String nonce = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.nonce");

        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("wechat_public_key_id",wechatpaySerial);
        NgMerchants ngMerchants =  ngMerchantsMapper.selectOne(queryWrapper);
        QueryWrapper queryWrappers = new QueryWrapper();
        queryWrappers.eq("platform_certificate_serial_number",wechatpaySerial);
        NgMerchants ngMerchants1 =  ngMerchantsMapper.selectOne(queryWrappers);
        boolean isVerified = false;
        if(ngMerchants!=null){
            String publicKey= wxPayConfig.getPublicKey(ngMerchants.getPublicKeyPath());//读取并预处理后的微信支付公钥
            isVerified = verifySignature(wechatpaySignature, wechatpayTimestamp, wechatpayNonce, json, publicKey);//微信支付公钥验签方式
        }else if (ngMerchants1!=null){
            String publicKey=wxPayConfig.getPublicKeyByCertificat(ngMerchants1.getPlatformCertificates());//读取预处理后的平台证书
            System.err.println("publickey:"+publicKey);
            isVerified = verifySignatureByCertificate(wechatpaySignature, wechatpayTimestamp, wechatpayNonce, json, publicKey);
        }else {
            System.out.println("解密失败");
            responseBody.put("code", "FAIL");
            responseBody.put("message", "解密失败");
            return new ResponseEntity<>(responseBody, HttpStatus.INTERNAL_SERVER_ERROR);
        }
        if (isVerified) {
            System.out.println("签名验证成功");


        } else {
            System.out.println("签名验证失败");
            responseBody.put("code", "FAIL");
            responseBody.put("message", "签名验证失败");

            // 返回500状态码以及符合要求的应答报文
            return new ResponseEntity<>(responseBody, HttpStatus.INTERNAL_SERVER_ERROR);
            //return error("签名验证失败");
        }


        /**
         * 验签字符串
         * 应答时间戳\n
         * 应答随机串\n
         * 应答报文主体\n
         */
        //String signStr=wechatpayTimestamp+"\n"+wechatpayNonce+"\n"+json+"\n";//验签字符串
        // 使用微信支付公钥验签
        /**
         * 验签方法
         * wechatpaySignature Wechatpay-Signature 响应头中的签名
         * wechatpayTimestamp Wechatpay-Timestamp 响应头中的时间戳
         * wechatpayNonce Wechatpay-Nonce 响应头中的随机串
         * responseBody 应答报文主体
         * publicKey 微信支付公钥（PEM 格式，去掉头尾并解码为二进制）
         */
        //微信支付公钥验签

        //平台证书验签
        NgMerchants resultl = ngMerchants != null ? ngMerchants : ngMerchants1;

        //使用apiv3key解密
        String decryptData="";
        try{
            decryptData= new AesUtil(resultl.getApiV3Key().getBytes(StandardCharsets.UTF_8)).decryptToString
                    (associated_data.getBytes(StandardCharsets.UTF_8),
                            nonce.getBytes(StandardCharsets.UTF_8),
                            ciphertext);
        }catch (Exception e)
        {
            System.out.println("解密失败");
            responseBody.put("code", "FAIL");
            responseBody.put("message", "解密失败");

            // 返回500状态码以及符合要求的应答报文
            return new ResponseEntity<>(responseBody, HttpStatus.INTERNAL_SERVER_ERROR);
            //return error("解密失败");
        }

        System.out.println("解密后的："+decryptData);
        if(decryptData==null)
        {
            System.out.println("解密后值为空");
            responseBody.put("code", "FAIL");
            responseBody.put("message", "解密后值为空");

            // 返回500状态码以及符合要求的应答报文
            return new ResponseEntity<>(responseBody, HttpStatus.INTERNAL_SERVER_ERROR);
            //return error("解密后值为空");
        }
        // 使用Hutool的JSONUtil解析JSON字符串
        JSONObject decryptJsonObject = JSONUtil.parseObj(decryptData);

        String result = decryptJsonObject.getStr("trade_state_desc");
        String trade_state=decryptJsonObject.getStr("trade_state");
        if(result.isEmpty() || ! result.equals("支付成功") || !trade_state.equals("SUCCESS") )
        {
            System.out.println("支付失败");
            responseBody.put("code", "FAIL");
            responseBody.put("message", "支付失败");

            // 返回500状态码以及符合要求的应答报文
            return new ResponseEntity<>(responseBody, HttpStatus.INTERNAL_SERVER_ERROR);
            //return error("支付失败");
        }
        //支付成功后
        JSONObject amount=decryptJsonObject.getJSONObject("amount");
        BigDecimal payer_total=amount.getBigDecimal("payer_total");//支付金额
        String rechargeCode=decryptJsonObject.getStr("out_trade_no");//订单号
        int flag=userRechargeCardsService.updateUserRechargeWechat(rechargeCode,payer_total);
        if(flag==1)
        {
            System.out.println("支付成功");
            //return success("支付成功");
            return new ResponseEntity<>(HttpStatus.OK);
        }
        System.out.println("修改数据库失败");
        responseBody.put("code", "FAIL");
        responseBody.put("message", "修改数据库失败");

        // 返回500状态码以及符合要求的应答报文
        return new ResponseEntity<>(responseBody, HttpStatus.INTERNAL_SERVER_ERROR);
        //return error("修改数据库失败");
    }

}
