package com.miaxis.app.controller.wx;


import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.miaxis.common.config.WxpayConfig;
import com.miaxis.common.constant.Constants;
import com.miaxis.common.core.domain.entity.UserInfo;
import com.miaxis.common.exception.CustomException;
import com.miaxis.common.utils.AesUtil;
import com.miaxis.common.utils.SecurityUtils;
import com.miaxis.user.service.IUserInfoService;
import com.miaxis.vip.domain.VipCode;
import com.miaxis.vip.service.IVipCodeService;
import com.miaxis.wx.domain.RefundRecord;
import com.miaxis.wx.domain.WxOrder;
import com.miaxis.wx.dto.WxNotifyReturnDTO;
import com.miaxis.wx.dto.WxpayNotifyDTO;
import com.miaxis.wx.service.IRefundRecordService;
import com.miaxis.wx.service.IWxOrderService;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Random;

@RestController
@RequiredArgsConstructor
@RequestMapping(Constants.OPEN_PREFIX+"/wx/notify")
@Api(tags = {"【app-微信回调】"})
@Slf4j
public class WxNotifyController {


    @Autowired
    private WxpayConfig wxpayConfig;


    @Autowired
    private IWxOrderService wxOrderService;

    @Autowired
    private IVipCodeService vipCodeService;


    @Autowired
    private IUserInfoService userInfoService;


    @Autowired
    private  IRefundRecordService  refundRecordService;

    @Autowired
    private AutoUpdateCertificatesVerifier verifier;


    /**
     * 微信支付回调接口
     */
    @PostMapping(value = "/wxpay")
    @ApiOperation("app微信支付回调")
    public WxNotifyReturnDTO wxpayNotify(@RequestBody WxpayNotifyDTO wxpayNotifyDTO, HttpServletRequest request) throws GeneralSecurityException, IOException {
//        String bodyString = getBodyString(request);
//        if (!validate(request,bodyString)){
//            throw new CustomException("签名失败");
//        }
        String resourceString = getSourString(wxpayNotifyDTO);
        log.info(resourceString);
        JSONObject jsonObject = JSONObject.parseObject(resourceString);
        //将回调数据写入数据库
        String outTradeNo = writeNotifyDataToDb(jsonObject);
        //创建兑换码
        String code = createCode();
        //激活用户
        avtivityCode(outTradeNo,code);

        WxNotifyReturnDTO wxNotifyReturnDTO = new WxNotifyReturnDTO();
        wxNotifyReturnDTO.setCode("SUCCESS");
        wxNotifyReturnDTO.setMessage("成功");
        return wxNotifyReturnDTO;
    }

    private void avtivityCode(String outTradeNo, String vipCode) {
        WxOrder wxOrder = wxOrderService.getByOutTradeNo(outTradeNo);
        if (wxOrder ==  null){
            throw new CustomException("改订单不存在");
        }
        UserInfo userInfo = userInfoService.getOne(new QueryWrapper<UserInfo>().eq("app_openid", wxOrder.getAppOpenid()));
        QueryWrapper<VipCode> queryWrapper = new QueryWrapper<VipCode>();
        queryWrapper.eq("vip_code", vipCode);
        VipCode vipCodeEntity = vipCodeService.getOne(queryWrapper);
        if (vipCodeEntity == null){
            throw new CustomException("该激活码不存在");
        }
        if (vipCodeEntity.getStatus() == 1){
            throw new CustomException("该激活码已被激活");
        }
        if (vipCodeEntity.getStatus() == 2){
            throw new CustomException("该激活码已作废");
        }
        vipCodeEntity.setStatus(1);
        vipCodeEntity.setActivationUserId(userInfo.getId());
        vipCodeEntity.setActivationTime(new Date());
        vipCodeService.updateById(vipCodeEntity);
        userInfo.setIsVip(1);
        userInfoService.updateById(userInfo);




    }

    private String createCode() {
        VipCode vipCode = new VipCode();
        String rVipCode = null ;
        do {
            rVipCode  = randomVipcode();
        }while (isRepeat(rVipCode));
        vipCode.setVipCode(rVipCode);
        vipCodeService.save(vipCode);
        return rVipCode;
    }

    private String getBodyString(HttpServletRequest request) {
            BufferedReader br = null;
            StringBuilder sb = new StringBuilder("");
            try
            {
                br = request.getReader();
                String str;
                while ((str = br.readLine()) != null)
                {
                    sb.append(str);
                }
                br.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            finally
            {
                if (null != br)
                {
                    try
                    {
                        br.close();
                    }
                    catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
            return sb.toString();

    }

    private Boolean validate(HttpServletRequest request, String bodyString) throws  NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        String sign = request.getHeader("Wechatpay-Signature");
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");

        StringBuffer sb = new StringBuffer();
        sb.append(timestamp + "\n");
        sb.append(nonce + "\n");
        sb.append(bodyString + "\n");
        X509Certificate validCertificate = verifier.getValidCertificate();
        // 进行签名服务
        Signature signature = Signature.getInstance("SHA256withRSA");
        // 用微信平台公钥对签名器进行初始化
        signature.initVerify(validCertificate);
        // 把我们构造的验签名串更新到签名器中
        signature.update(sb.toString().getBytes(StandardCharsets.UTF_8));
        Boolean result = signature.verify(Base64Utils.decodeFromString(sign));
        log.info("微信支付回调验签:"+result.toString());
        return result;

    }


    /**
     * 微信退款回调
     */
    @PostMapping(value = "/refund")
    @ApiOperation("微信退款回调")
    public WxNotifyReturnDTO refundNotify(@RequestBody WxpayNotifyDTO wxpayNotifyDTO, HttpServletRequest request) throws GeneralSecurityException, IOException {
        String bodyString = getBodyString(request);
        if (!validate(request,bodyString)){
            throw new CustomException("签名失败");
        }
        String resourceString = getSourString(wxpayNotifyDTO);
        log.info(resourceString);
        JSONObject jsonObject = JSONObject.parseObject(resourceString);
        //将回调数据写入数据库
        writeRefundNotifyDataToDb(jsonObject);

        WxNotifyReturnDTO wxNotifyReturnDTO = new WxNotifyReturnDTO();
        wxNotifyReturnDTO.setCode("SUCCESS");
        wxNotifyReturnDTO.setMessage("成功");

        return wxNotifyReturnDTO;
    }

    private void writeRefundNotifyDataToDb(JSONObject jsonObject) {
        String refundId = jsonObject.getString("refund_id");
        RefundRecord refundRecord = refundRecordService.getByRefundId(refundId);
        if (refundRecord == null) {
            throw new CustomException("该退款订单不存在");
        }
        refundRecord.setTransactionId(jsonObject.getString("transaction_id"));
        refundRecord.setUserReceivedAccount(jsonObject.getString("user_received_account"));
        refundRecord.setStatus(jsonObject.getString("refund_status"));
        refundRecordService.updateById(refundRecord);


    }






    public String writeNotifyDataToDb(JSONObject jsonObject) {
        String outTradeNo = jsonObject.getString("out_trade_no");
        WxOrder wxOrder = wxOrderService.getByOutTradeNo(outTradeNo);
        if (wxOrder == null) {
            throw new CustomException("该订单不存在");
        }
        wxOrder.setTransactionId(jsonObject.getString("transaction_id"));
        JSONObject amount = jsonObject.getJSONObject("amount");
        wxOrder.setPayerTotal(amount.getInteger("payer_total"));
        wxOrder.setTotal(amount.getInteger("total"));
        wxOrder.setCurrency(amount.getString("currency"));
        wxOrder.setPayerCurrency(amount.getString("payer_currency"));
        wxOrder.setTradeState(jsonObject.getString("trade_state"));
        wxOrder.setBankType(jsonObject.getString("bank_type"));
        DateTime dateTime  = new DateTime(jsonObject.getString("success_time"));
        wxOrder.setSuccessTime(dateTime.toDate());
        wxOrder.setTradeStateDesc(jsonObject.getString("trade_state_desc"));
        wxOrder.setTradeType(jsonObject.getString("trade_type"));
        wxOrder.setAttach(jsonObject.getString("attach"));
        JSONObject sceneInfo = jsonObject.getJSONObject("scene_info");
        if (sceneInfo != null){
            wxOrder.setDeviceId(sceneInfo.getString("device_id"));
        }
        wxOrderService.updateById(wxOrder);
        return outTradeNo;

    }

    private String getSourString(WxpayNotifyDTO wxpayNotifyDTO) throws GeneralSecurityException, IOException {
        AesUtil aesUtil = new AesUtil(wxpayConfig.getV3key().getBytes());
        WxpayNotifyDTO.WxpaySource wxpaySource = wxpayNotifyDTO.getResource();
        return aesUtil.decryptToString(wxpaySource.getAssociated_data().getBytes(), wxpaySource.getNonce().getBytes(), wxpaySource.getCiphertext());
    }

    private String randomVipcode() {
        String result = "";
        Random random = new Random();
        for(int i =0 ;i <11 ;i ++){
            result+=random.nextInt(10);
        }
        return  result;


    }
    private boolean isRepeat(String rVipCode) {
        QueryWrapper<VipCode> wrapper = new QueryWrapper<VipCode>();
        wrapper.eq("vip_code",rVipCode);
        VipCode one = vipCodeService.getOne(wrapper);
        return one!= null;

    }






}
