package com.ruoyi.framework.web.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.QRCodeUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.sign.Base64;
import com.ruoyi.system.domain.Customer;
import com.ruoyi.system.domain.Order;
import com.ruoyi.system.mapper.CustomerMapper;

import com.ruoyi.system.mapper.OrderMapper;
import com.ruoyi.system.weixin.entity.OrderInfo;
import com.ruoyi.system.weixin.entity.OrderReturnInfo;
import com.ruoyi.system.weixin.entity.QueryReturnInfo;
import com.ruoyi.system.weixin.entity.SignInfo;
import com.ruoyi.system.weixin.util.PayUtil;
import com.ruoyi.system.weixin.util.RandomStringGenerator;
import com.ruoyi.system.weixin.util.Signature;
import com.thoughtworks.xstream.XStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Service
public class WeixinServiceImpl  {

    private static final Logger log = LoggerFactory.getLogger(WeixinServiceImpl.class);

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private CustomerMapper customerMapper;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private OrderMapper orderMapper;


    @Value("${weixin.appid}")
    private String appid;

    @Value("${weixin.secret}")
    private String secret;
    @Value("${weixin.mchid}")
    private String mchId;
    @Value("${weixin.key}")
    private String key;
    @Value("${weixin.notify-url}")
    private String notifyUrl;
    @Value("${weixin.trade-type}")
    private String tradeType;
    @Value("${weixin.url}")
    private String url;
    @Value("${weixin.query-url}")
    private String queryUrl;

    @Value("${weixin.cert-path}")
    private String certPath;

    public String getSessionKeyOrOpenId(String code) {
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
        String replaceUrl = url.replace("{0}", appid).replace("{1}", secret).replace("{2}", code);
        String res = restTemplate.getForObject(url,String.class);
        return res;
    }

    public String  login(String code, String encryptedData, String iv, String source) {
        try{
            String sessionKeyOrOpenId = getSessionKeyOrOpenId(code);
            JSONObject openInfo = JSONObject.parseObject(sessionKeyOrOpenId);
            String cutomerId = openInfo.getString("openid");
            String encData = this.wxDecrypt(encryptedData, sessionKeyOrOpenId, iv);
            Customer customer = customerMapper.selectCustomerById(cutomerId);
            JSONObject encDataInf = JSONObject.parseObject(encData);
            if(customer ==null){
                customer = new Customer();
                customer.setId(cutomerId);
                customer.setHeadSculpturePath(encDataInf.getString("avatarUrl"));
                customer.setUserName(encDataInf.getString("nickName"));
                customer.setCreateTime(new Date());
                if(StringUtils.isNotEmpty(source)){
                    customer.setSoucre(source);
                }
                customerMapper.insertCustomer(customer);
            }

            return tokenService.createToken(transLoginUser(customer,encData));
        }catch (Exception e){
            log.error("微信登录发生异常",e);
            throw new ServiceException("微信登录异常，请重新登录");
        }


    }
    private LoginUser transLoginUser(Customer customer,String encData){
        LoginUser loginUser = new LoginUser();
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setOpenId(customer.getId());
        loginUser.setExt(encData);
        return loginUser;

    }

    public String wxDecrypt(String encryptedData, String json, String vi) throws Exception {
        // 开始解密
        //：{"session_key":"G59Evf\/Em54X6WsFsrpA1g==","openid":"o2ttv5L2yufc4-VoSPhTyUnToY60"}
        JSONObject jsonObject = JSON.parseObject(json);
        String sessionKey = (String) jsonObject.get("session_key");
        byte[] encData = Base64.decode(encryptedData);
        byte[] iv = Base64.decode(vi);
        byte[] key = Base64.decode(sessionKey);
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        return new String(cipher.doFinal(encData), "UTF-8");
    }

    /**
     * 统一下单接口
     * money 分为单位
     * @return
     */
    public Map payment(Integer money,String goodsName,String orderNo,String openId) {

        try {
            OrderInfo order = new OrderInfo();
            order.setAppid(appid);
            order.setMch_id(mchId);
            order.setNonce_str(RandomStringGenerator.getRandomStringByLength(32));
            order.setBody(goodsName);
            order.setOut_trade_no(orderNo);
            order.setTotal_fee(money);     // 该金钱其实10 是 0.1元
            order.setSpbill_create_ip(IpUtils.getHostIp());
            order.setNotify_url(notifyUrl);
            order.setTrade_type(tradeType);
            //这里直接使用当前用户的openid
            order.setOpenid(openId);
            order.setSign_type("MD5");
            //生成签名
            String sign = Signature.getSign(order,key);
            order.setSign(sign);

            String result = restTemplate.postForObject(url, order, String.class);
            System.out.println(result);
            XStream xStream = new XStream();
            xStream.alias("xml", OrderReturnInfo.class);

            OrderReturnInfo returnInfo = (OrderReturnInfo) xStream.fromXML(result);
            // 二次签名
            if ("SUCCESS".equals(returnInfo.getReturn_code()) && returnInfo.getReturn_code().equals(returnInfo.getResult_code())) {
                SignInfo signInfo = new SignInfo();
                signInfo.setAppId(appid);
                long time = System.currentTimeMillis() / 1000;
                signInfo.setTimeStamp(String.valueOf(time));
                signInfo.setNonceStr(RandomStringGenerator.getRandomStringByLength(32));
                signInfo.setRepay_id("prepay_id=" + returnInfo.getPrepay_id());
                signInfo.setSignType("MD5");
                //生成签名
                String sign1 = Signature.getSign(signInfo,key);
                Map<String, String> payInfo = new HashMap<>();
                payInfo.put("timeStamp", signInfo.getTimeStamp());
                payInfo.put("nonceStr", signInfo.getNonceStr());
                payInfo.put("package", signInfo.getRepay_id());
                payInfo.put("signType", signInfo.getSignType());
                payInfo.put("paySign", sign1);
                // 此处可以写唤起支付前的业务逻辑
                // 业务逻辑结束 回传给小程序端唤起支付
                return payInfo;
            }
            return null;
        } catch (Exception e) {
            log.error("微信统一下单发生异常【{}】",e);
            throw new ServiceException("微信统一下单发生异常");
        }

    }

    public void wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream()));
        String line = null;
        StringBuilder sb = new StringBuilder();
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }
        br.close();
        //sb为微信返回的xml
        String notityXml = sb.toString();
        String resXml = "";
        System.out.println("接收到的报文：" + notityXml);

        Map map = PayUtil.doXMLParse(notityXml);

        String returnCode = (String) map.get("return_code");
        if ("SUCCESS".equals(returnCode)) {
            //验证签名是否正确
            Map<String, String> validParams = PayUtil.paraFilter(map);  //回调验签时需要去除sign和空值参数
            String validStr = PayUtil.createLinkString(validParams);//把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
            String sign = PayUtil.sign(validStr, key, "utf-8").toUpperCase();//拼装生成服务器端验证的签名
            // 因为微信回调会有八次之多,所以当第一次回调成功了,那么我们就不再执行逻辑了

            //根据微信官网的介绍，此处不仅对回调的参数进行验签，还需要对返回的金额与系统订单的金额进行比对等
            if (sign.equals(map.get("sign"))) {

                String out_trade_no = (String) map.get("out_trade_no");
                Order order = orderMapper.selectOrderById(out_trade_no);
                if(order ==null) {
                    throw new ServiceException("不存在该订单：" + out_trade_no);
                }
                if("1".equals(order.getState())){
                    order.setState("2");
                    order.setPayTime(new Date());
                    order.setUpdatedAt(new Date());
                    order.setPickCode(QRCodeUtil.getBase64QRCode(JSONObject.toJSONString(order)));
                    orderMapper.updateOrder(order);
                    //异步推送信息给机器

                }
                //通知微信服务器已经支付成功
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
            } else {
                System.out.println("微信支付回调失败!签名不一致");
            }
        } else {
            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                    + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
        }
        System.out.println(resXml);
        System.out.println("微信支付回调数据结束");

        BufferedOutputStream out = new BufferedOutputStream(
                response.getOutputStream());
        out.write(resXml.getBytes());
        out.flush();
        out.close();
    }
    public Map orderQuery() {
        Map<String, Object> map = new HashMap<>();

        try {
            OrderInfo order = new OrderInfo();
            order.setAppid(appid);
            order.setMch_id(mchId);
            order.setNonce_str(RandomStringGenerator.getRandomStringByLength(32));
            order.setOut_trade_no("wuyye9w8akwbq1nc60o4affwwqnxh9dn");
            //order.setSign_type("MD5");
            //生成签名
            String sign = Signature.getSign(order,key);
            order.setSign(sign);
            String result = restTemplate.postForObject(queryUrl,order,String.class);
            System.out.println(result);
            XStream xStream = new XStream();
            xStream.alias("xml", QueryReturnInfo.class);

            QueryReturnInfo returnInfo = (QueryReturnInfo) xStream.fromXML(result);
            map.put("status", 500);
            map.put("msg", "统一下单失败!");
            map.put("data", returnInfo);
            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
