package com.hc.web.controller.browser.app;

import com.alibaba.fastjson.JSONObject;
import com.hc.business.dto.ZccmClassCourseDto;
import com.hc.business.dto.ZccmUserCourseDto;
import com.hc.business.mapper.*;
import com.hc.business.model.*;
import com.hc.core.utils.properties.PayProperties;
import com.hc.core.utils.result.Result;
import com.hc.core.utils.status.CourseFlagEnum;
import com.hc.core.utils.status.OrdersFlagEnum;
import com.hc.core.utils.wx.StringUtils;
import com.hc.core.utils.wx.XMLUtils;
import com.hc.web.config.util.HttpRequest;
import com.hc.web.config.util.HttpsUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author 10696
 */

@RestController
@RequestMapping("/new/api/jsApi")
public class AppWxPayJsApiController {

    private final PayProperties payProperties;

    private final ZccmOrdersMapper ordersMapper;

    @Autowired
    private ZccmOrderUserLogMapper orderUserLogMapper;

    @Autowired
    private ZccmUserCourseMapper userCourseMapper;

    @Autowired
    private ZccmClassCourseMapper classCourseMapper;

    @Autowired
    private ZccmUserStatusMapper userStatusMapper;

    @Autowired
    private ZccmUserAdviseMapper userAdviseMapper;

    private final String secret = "510742935518af9b4b2d5b3aab0d378d";

    Log log = LogFactory.getLog(this.getClass());

    public AppWxPayJsApiController(PayProperties payProperties, ZccmOrdersMapper ordersMapper) {
        this.payProperties = payProperties;
        this.ordersMapper = ordersMapper;
    }


    @PostMapping(value = "/pay", produces = {"application/json;charset=UTF-8"})
    public Result zf1(HttpServletRequest request, HttpServletResponse response) {

        String getopenid_url = "https://api.weixin.qq.com/sns/oauth2/access_token";

        String sign_Key = payProperties.getWxPay().getSign_key();

        String code = request.getParameter("code");
        String param =
                "appid=" + payProperties.getWxPay().getAppid() +
                        "&secret=" + secret
                        + "&code=" + code + "&grant_type=authorization_code";
        //向微信服务器发送get请求获取openIdStr
        String openIdStr = HttpRequest.sendGet(getopenid_url, param);
        //转成Json格式
        JSONObject json = JSONObject.parseObject(openIdStr);
        String openId = json.getString("openid");

        Map<String, Object> returnMap = new HashMap<String, Object>();
        String appId = payProperties.getWxPay().getAppid();
        String body = request.getParameter("body");
        String tradeNo = request.getParameter("out_trade_no");
        String merchantId = payProperties.getWxPay().getMch_id();
        String nonceStr1 = createNonceStr();
        String notifyUrl = "http://zgcj.cfefe.com/new/api/jsApi/notify";

        //不可为小数，否则报错//不可为小数，否则报错
        String totalFee = request.getParameter("total_amount");
        String finalMoney = StringUtils.getMoney(totalFee);
        TreeMap<String, String> map = new TreeMap<>();
        map.put("appid", appId);
        map.put("mch_id", merchantId);
        map.put("device_info", "WEB");
        map.put("body", body);
        map.put("trade_type", "JSAPI");
        map.put("nonce_str", nonceStr1);
        map.put("notify_url", notifyUrl);
        map.put("out_trade_no", tradeNo);
        map.put("total_fee", finalMoney);
        map.put("openid", openId);
        String sign = createSign(map);

        String xml = "<xml>" +
                "<appid>" + appId + "</appid>" +
                "<body>" + body + "</body>" +
                "<device_info>WEB</device_info>" +
                "<mch_id>" + merchantId + "</mch_id>" +
                "<nonce_str>" + nonceStr1 + "</nonce_str>" +
                "<notify_url>" + notifyUrl + "</notify_url>" +
                "<openid>" + openId + "</openid>" +
                "<out_trade_no>" + tradeNo + "</out_trade_no>" +
                "<total_fee>" + finalMoney + "</total_fee>" +
                "<trade_type>JSAPI</trade_type>" +
                "<sign>" + sign + "</sign>" +
                "</xml>";
        String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

        String result = null;

        result = HttpsUtil.httpsRequestToString(url, "POST", xml);

        String reg = "<prepay_id><!\\[CDATA\\[(\\w+)\\]\\]></prepay_id>";

        Pattern pattern = Pattern.compile(reg);

        Matcher matcher = pattern.matcher(result);

        String prepayId = "";

        while (matcher.find()) {
            prepayId = matcher.group(1);
            System.out.println("预支付ID：" + prepayId);
        }

        Date beijingDate = Calendar.getInstance(Locale.CHINA).getTime();
        String datassss = String.valueOf(beijingDate.getTime() / 1000);
        String nonceStrss = createNonceStr();
        //appId, timeStamp, nonceStr, package, signType
        //再一次签名，我看网上的有好多都没说，是后来报错了我搜索才发现要重新签名
        String stringAs = "appId=" + payProperties.getWxPay().getAppid() +
                "&nonceStr=" + nonceStrss + "&package=prepay_id=" + prepayId +
                "&signType=MD5&timeStamp=" + datassss + "&key=" +
                sign_Key;

        String dd = null;
        try {
            dd = MD5(stringAs).toUpperCase();
        } catch (Exception e) {
            e.printStackTrace();
        }

        returnMap.put("paySignss", dd);
        returnMap.put("appids", payProperties.getWxPay().getAppid());
        returnMap.put("timestampss", datassss);
        returnMap.put("nonceStrss", nonceStrss);
        returnMap.put("packagess", "prepay_id=" + prepayId);
        returnMap.put("signTypess", "MD5");

        return Result.success(returnMap);
    }


    @PostMapping(value = "/notify")
    public void weixinPayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        BufferedReader reader = request.getReader();
        String line = "";
        Map map = new HashMap<>();
        String xml = "<xml><return_code><![CDATA[FAIL]]></xml>";
        StringBuffer inputString = new StringBuffer();
        while ((line = reader.readLine()) != null) {
            inputString.append(line);
        }
        request.getReader().close();
        if (inputString.toString().length() > 0) {
            map = XMLUtils.parseXmlToList(inputString.toString());
        } else {
            log.info("接受微信报文为空");
        }
        if (map != null && "SUCCESS".equals(map.get("result_code"))) {
            String out_trade_no = (String) map.get("out_trade_no");
            String transaction_id = (String) map.get("transaction_id");
            ZccmOrders orders =
                    ordersMapper.queryByOrderNo(out_trade_no);
            if (Objects.isNull(orders)) {
                return;
            }
            orders.setOrderStatus(OrdersFlagEnum.ORDERS_FLAG_PAID.getCode());
            orders.setTradeNo(transaction_id);
            ordersMapper.updateByPrimaryKey(orders);

            //查询订单中的用户
            Integer ordersId = orders.getOrdersId();
            Integer classId = orders.getClassId();
            List<ZccmOrderUserLog> orderUserLogs =
                    orderUserLogMapper.queryByOrderId(ordersId);
            List<ZccmClassCourseDto> classCourseDtoList =
                    classCourseMapper.queryByClassId(classId, CourseFlagEnum.COURSE_REQUIRE_CODE.getCode());

            if (CollectionUtils.isEmpty(orderUserLogs)) {
                if (CollectionUtils.isEmpty(classCourseDtoList)) {
                    return;
                }
                return;
            }

            createUserAdvise(orderUserLogs);
            createUserCourse(classId, orderUserLogs, classCourseDtoList);

            xml = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
        } else {
            //失败的业务。。。
        }
        //告诉微信端已经确认支付成功
        response.getWriter().write(xml);
    }

    /**
     * 生成 MD5
     *
     * @param data 待处理数据
     * @return MD5结果
     */
    private static String MD5(String data) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes(StandardCharsets.UTF_8));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 生成随机数
     * <p>算法参考：https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3</p>
     *
     * @return 随机数字符串
     */

    private static String createNonceStr() {
        SecureRandom random = new SecureRandom();
        int randomNum = random.nextInt();
        return Integer.toString(randomNum);
    }

    /**
     * 生成签名，用于在微信支付前，获取预支付时候需要使用的参数sign
     * <p>算法参考：https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3</p>
     *
     * @param params 需要发送的所有数据设置为的Map
     * @return 签名sign
     */

    private String createSign(TreeMap<String, String> params) {
        String signValue = "";
        String stringSignTemp = "";

        String stringA = "";
        //获得stringA
        Set<String> keys = params.keySet();
        for (String key : keys) {
            stringA += (key + "=" + params.get(key) + "&");
        }
        stringA = stringA.substring(0, stringA.length() - 1);
        //获得stringSignTemp
        stringSignTemp = stringA + "&key=" + payProperties.getWxPay().getSign_key();
        //获得signValue
        signValue = encryptByMD5(stringSignTemp).toUpperCase();
        return signValue;
    }

    /**
     * MD5加密
     *
     * @param sourceStr
     * @return
     */

    private static String encryptByMD5(String sourceStr) {
        String result = "";
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(sourceStr.getBytes(StandardCharsets.UTF_8));
            byte b[] = md.digest();
            int i;
            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < b.length; offset++) {
                i = b[offset];
                if (i < 0) {
                    i += 256;
                }
                if (i < 16) {
                    buf.append("0");
                }
                buf.append(Integer.toHexString(i));
            }
            result = buf.toString();
        } catch (NoSuchAlgorithmException e) {
            System.out.println(e);
        }
        return result;
    }

    private void createUserAdvise(List<ZccmOrderUserLog> orderUserLogs) {
        orderUserLogs.forEach(orderUserLog -> {
            ZccmUserAdvise userAdvise = new ZccmUserAdvise();
            userAdvise.setAdviceType(1);
            userAdvise.setCreateTime(System.currentTimeMillis());
            userAdvise.setAdvise("报名成功,这里是一行通知。。。。。");
            userAdvise.setUserId(orderUserLog.getReUserId());
            userAdvise.setTitle("标题");
            userAdviseMapper.insert(userAdvise);
        });
    }

    private void createUserCourse(Integer classId,
                                  List<ZccmOrderUserLog> orderUserLogs,
                                  List<ZccmClassCourseDto> classCourseDtoList) {

        orderUserLogs.forEach(orderUserLog -> {
            Integer reUserId = orderUserLog.getReUserId();
            Integer ordersId = orderUserLog.getOrderId();
            //开用户状态
            List<ZccmUserStatus> userStatuses =
                    userStatusMapper.queryByClassIdAndUserId(classId, reUserId);
            if (CollectionUtils.isEmpty(userStatuses)) {
                ZccmUserStatus userStatus = new ZccmUserStatus();
                userStatus.setClassId(classId);
                userStatus.setOrderId(ordersId);
                userStatus.setUserId(reUserId);
                userStatus.setStatus(OrdersFlagEnum.ORDERS_FLAG_PAID.getCode());
                userStatusMapper.insert(userStatus);
            }
            //课程
            classCourseDtoList.forEach(classCourseDto -> {
                Integer courseId = classCourseDto.getCourseId();
                //是否已开
                List<ZccmUserCourseDto> userCourses
                        = userCourseMapper.selectUserCourseProgress(reUserId, classId, courseId);
                if (CollectionUtils.isEmpty(userCourses)) {
                    //开课
                    ZccmUserCourse userCourse = new ZccmUserCourse();
                    userCourse.setClassId(classId);
                    userCourse.setCourseId(courseId);
                    userCourse.setUserId(reUserId);
                    userCourse.setCoursePercent(0.00);
                    userCourseMapper.insert(userCourse);
                }
            });
        });
    }


}
