package com.zhongzheng.modules.wx.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.Page;
import com.zhongzheng.common.config.WxSmallConfig;
import com.zhongzheng.common.core.redis.RedisCache;
import com.zhongzheng.common.exception.CustomException;
import com.zhongzheng.common.utils.AES;
import com.zhongzheng.common.utils.DateUtils;
import com.zhongzheng.common.utils.ServletUtils;
import com.zhongzheng.common.utils.SnowflakeIdUtils;
import com.zhongzheng.common.utils.http.HttpUtils;
import com.zhongzheng.common.utils.ip.IpUtils;
import com.zhongzheng.common.utils.polyv.PolyvUtils;
import com.zhongzheng.common.utils.wxpay.*;
import com.zhongzheng.modules.base.service.IShoppingCartService;
import com.zhongzheng.modules.course.domain.Course;
import com.zhongzheng.modules.course.service.ICourseService;
import com.zhongzheng.modules.exam.domain.ExamNumber;
import com.zhongzheng.modules.exam.service.IExamNoteService;

import com.zhongzheng.modules.goods.service.IGoodsService;
import com.zhongzheng.modules.goods.vo.GoodsVo;
import com.zhongzheng.modules.grade.domain.ClassGradeUserTemp;
import com.zhongzheng.modules.grade.service.IClassGradeUserTempService;
import com.zhongzheng.modules.order.bo.OrderGoodsQueryBo;
import com.zhongzheng.modules.order.domain.Order;
import com.zhongzheng.modules.order.domain.OrderGoods;
import com.zhongzheng.modules.order.service.IOrderGoodsService;
import com.zhongzheng.modules.order.service.IOrderService;
import com.zhongzheng.modules.order.vo.OrderGoodsVo;
import com.zhongzheng.modules.polyv.bo.PolyvVideoAddBo;
import com.zhongzheng.modules.polyv.bo.PolyvVideoEditBo;
import com.zhongzheng.modules.polyv.bo.PolyvVideoQueryBo;
import com.zhongzheng.modules.polyv.domain.PolyvVideDo;
import com.zhongzheng.modules.polyv.domain.PolyvVideo;
import com.zhongzheng.modules.polyv.domain.TokenResponse;
import com.zhongzheng.modules.polyv.mapper.PolyvVideoMapper;
import com.zhongzheng.modules.polyv.service.IPolyvVideoService;
import com.zhongzheng.modules.polyv.vo.PolyvVideoQuerVo;
import com.zhongzheng.modules.polyv.vo.PolyvVideoVo;
import com.zhongzheng.modules.user.bo.OrderPossessUserAddBo;
import com.zhongzheng.modules.user.bo.UserExamGoodsAddBo;
import com.zhongzheng.modules.user.bo.UserExamGoodsQueryBo;
import com.zhongzheng.modules.user.bo.UserExamGoodsSupplementAddBo;
import com.zhongzheng.modules.user.domain.User;
import com.zhongzheng.modules.user.domain.UserExamGoods;
import com.zhongzheng.modules.user.service.IOrderPossessUserService;
import com.zhongzheng.modules.user.service.IUserExamGoodsService;
import com.zhongzheng.modules.user.service.IUserExamGoodsSupplementService;
import com.zhongzheng.modules.user.service.IUserService;
import com.zhongzheng.modules.user.vo.UserExamGoodsVo;
import com.zhongzheng.modules.wx.service.IWxPayService;
import com.zhongzheng.modules.wx.vo.WxPayVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.squareup.okhttp.internal.Util.md5Hex;

/**
 * 保利威视频信息Service业务层处理
 *
 * @author ruoyi
 * @date 2021-06-11
 */
@Service
public class WxPayServiceImpl  implements IWxPayService {

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

    @Value("${wx.gzh.appid}")
    private String gzhAppid;

    @Value("${wx.gzh.appsecret}")
    private String gzh_appsrcret;

    @Value("${wx.wepay.mchid}")
    private String mchid;

    @Value("${wx.wepay.key}")
    private String key;

    @Value("${wx.wepay.appsecret}")
    private String appsecret;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private IUserExamGoodsService userExamGoodsService;

    @Autowired
    private IUserExamGoodsSupplementService userExamGoodsSupplementService;

    private WXPayConstants.SignType signType;

 //   private String notifyUrl = "https://api.xyyxt.net/wx/pay/callback";

    private String notifyUrl = "http://42.192.164.187:19005/wx/pay/callback";

    private WXPayRequest wxPayRequest;

    private boolean autoReport = false;

    @Autowired
    private IClassGradeUserTempService iClassGradeUserTempService;

    @Autowired
    private IOrderPossessUserService iOrderPossessUserService;

    @Autowired
    private IOrderService iOrderService;

    @Autowired
    private ICourseService iCourseService;

    @Autowired
    private IUserService iUserService;

    @Autowired
    private IOrderGoodsService iOrderGoodsService;

    @Autowired
    private IGoodsService iGoodsService;




    private String gzh_tokenUrl = "https://api.weixin.qq.com/cgi-bin/token";

    private String gzh_tokenParam = "grant_type=client_credential&appid=%s&secret=%s";

    @Override
    public Map<String, String> payment(String out_trade_no,String openid,String body,String price) {
        WxSmallConfig config = new WxSmallConfig(appid,mchid,key);
        try {
            WXPay wxpay = new WXPay(config);
            Map<String, String> data = new HashMap<String, String>();
            data.put("body", body);
            data.put("out_trade_no", out_trade_no);
            data.put("device_info", "");
            data.put("fee_type", "CNY");
            data.put("total_fee", "1");
            data.put("openid", openid);
            data.put("spbill_create_ip", IpUtils.getIpAddr(ServletUtils.getRequest()));
            data.put("notify_url", notifyUrl);
            data.put("trade_type", "JSAPI");
            String TenantId = ServletUtils.getRequest().getHeader("TenantId");
            data.put("attach", TenantId);
            Map<String, String> resp = wxpay.unifiedOrder(data);

            Map<String, String> result = new HashMap<>();
   //         result.put("provider","wxpay");
            result.put("appId",appid);
            result.put("timeStamp",String.valueOf(DateUtils.getNowTime()));
            result.put("nonceStr",resp.get("nonce_str"));
            result.put("package","prepay_id="+resp.get("prepay_id"));
            result.put("signType","MD5");
            result.put("sign", WXPayUtil.generateSignature(result, key, WXPayConstants.SignType.MD5));
     //       System.out.println(resp);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    public Map<String, String> paymentGzh(String out_trade_no, String openid, String body, BigDecimal price, String url) {
        WxSmallConfig config = new WxSmallConfig(gzhAppid,mchid,key);
        try {
            WXPay wxpay = new WXPay(config);
            BigDecimal unit = new BigDecimal(100);
            price = price.multiply(unit);
            Map<String, String> data = new HashMap<String, String>();
            data.put("body", body);
            data.put("out_trade_no", out_trade_no);
            data.put("device_info", "");
            data.put("fee_type", "CNY");
            data.put("total_fee", price.intValue()+"");
            data.put("openid", openid);
            data.put("spbill_create_ip", IpUtils.getIpAddr(ServletUtils.getRequest()));
            data.put("notify_url", notifyUrl);
            data.put("trade_type", "JSAPI");
            String TenantId = ServletUtils.getRequest().getHeader("TenantId");
            data.put("attach", TenantId);
            Map<String, String> resp = wxpay.unifiedOrder(data);
            Map<String, String> result = new HashMap<>();
            //         result.put("provider","wxpay");
            result.put("appId",gzhAppid);
            result.put("timeStamp",String.valueOf(DateUtils.getNowTime()));
            result.put("nonceStr",resp.get("nonce_str"));
            result.put("package","prepay_id="+resp.get("prepay_id"));
            result.put("signType","MD5");
            result.put("paySign", WXPayUtil.generateSignature(result, key, WXPayConstants.SignType.MD5));
            result.put("configPaySign", wx_config_sign(result,url));
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException("错误"+e.getMessage());
        }

    //    return null;
    }

    public String wx_config_sign(Map<String, String> result,String url) {
        String jsapi_ticket = wx_get_jsapi_ticket();
        if(!Validator.isNotNull(jsapi_ticket)){
            throw new CustomException("jsapi_ticket错误"+jsapi_ticket);
        }
        String str = "jsapi_ticket="+jsapi_ticket+"&noncestr="+result.get("nonceStr")+"&timestamp="+result.get("timeStamp")+"&url="+url;
        String signature = AES.SHA1(str);
        return signature;
    }

    @Override
    public String paymentCallBack(String notifyData) {
        String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml> ";
        WxSmallConfig config = new WxSmallConfig(appid,mchid,key);
        try {
            WXPay wxpay = new WXPay(config);

            Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);  // 转换成map

            if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
                String result_code = notifyMap.get("result_code");
                if("SUCCESS".equals(result_code)){
                    String out_trade_no = notifyMap.get("out_trade_no");
                    String transaction_id = notifyMap.get("transaction_id");
                    String TenantId = notifyMap.get("attach");
                    ServletUtils.getRequestAttributes().getResponse().setHeader("TenantId",TenantId);
                    if(dealOrder(out_trade_no,transaction_id)){
                        xmlBack = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
                        return xmlBack;
                    }
                }
            }
            else {
                // 签名错误，如果数据里没有sign字段，也认为是签名错误
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return xmlBack;
    }


    public String wx_get_jsapi_ticket() {
        String key = "gzh_ticket";
        String ticket = redisCache.getCacheObject(key);
        if(!Validator.isNotNull(ticket)){
            String access_token = wx_get_token();
            String param = String.format("access_token=%s&type=jsapi", access_token);
            String resultString  = HttpUtils.sendGet("https://api.weixin.qq.com/cgi-bin/ticket/getticket",param);
            JSONObject jsonObject = (JSONObject) JSONObject.parse(resultString);
            ticket = String.valueOf(jsonObject.get("ticket"));
            if(ticket!=null){
                redisCache.setCacheObject(key, ticket, 2*50, TimeUnit.MINUTES);//2个小时
            }
        }
        return ticket;
    }

    //获取微信公众号access_token
    public String wx_get_token()
    {
        String key = "gzh_access_token";
        String access_token = redisCache.getCacheObject(key);
        if(!Validator.isNotNull(access_token)){
            String param = String.format(gzh_tokenParam, gzhAppid, gzh_appsrcret);
            String resultString  = HttpUtils.sendGet(gzh_tokenUrl,param);
            //解析json
            JSONObject jsonObject = (JSONObject) JSONObject.parse(resultString);
            access_token = String.valueOf(jsonObject.get("access_token"));  //这里应该把access_token缓存起来，至于要怎么缓存就看各位了，有效期是7200s
            if(access_token!=null){
                redisCache.setCacheObject(key, access_token, 2*50, TimeUnit.MINUTES);//2个小时
            }
        }
        if(!Validator.isNotNull(access_token)){
            throw new CustomException("access_token错误"+access_token);
        }
        return access_token;
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean dealOrder(String out_trade_no,String transaction_id){
        Order order = iOrderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderSn, out_trade_no));
        if(order!=null&&order.getOrderStatus()==0){
            order.setOrderStatus(Order.PAY_FULL);
            order.setPayTime(DateUtils.getNowTime());
            order.setTransid(transaction_id);
            order.setOrderGeneral(out_trade_no);
            order.setStatus(1);
            if(iOrderService.updateById(order)){
                Map<String,Object> map = new HashMap<>();
                map.put("order_sn",out_trade_no);
                List<OrderGoods> goodsList = iOrderGoodsService.listByMap(map);
                for (OrderGoods g : goodsList) {
                    g.setPayStatus(OrderGoods.PAY_FULL);
                    g.setGoodsReceived(g.getGoodsRealPrice());
                    iOrderGoodsService.updateById(g);
                    this.joinLockGrade(order.getOrderSn(),g.getGoodsId(),g.getOrderGoodsId());
                    //给用户增加商品考试次数前培次数
                    updateUserExamGoods(g,order);
                }
            }

        }
        return true;
    }

    private void updateUserExamGoods(OrderGoods g,Order order) {
        GoodsVo goodsVo = iGoodsService.queryById(g.getGoodsId());
        //添加商品考试次数和补考次数
        if (goodsVo.getGoodsType().equals(1)){
            if (userExamGoodsService.queryExamGoods(goodsVo.getGoodsId(),order.getUserId()) < 1){
                ExamNumber examNumber = userExamGoodsService.queryExam(goodsVo.getGoodsId());
                if (examNumber != null) {
                    UserExamGoods add = new UserExamGoods();
                    add.setGoodsId(g.getGoodsId());
                    add.setUserId(order.getUserId());
                    add.setExamNumber(examNumber.getExamNumber());
                    add.setDoNumber(examNumber.getDoNumber());
                    add.setCreateTime(DateUtils.getNowTime());
                    add.setUpdateTime(DateUtils.getNowTime());
                    userExamGoodsService.save(add);
                    UserExamGoodsSupplementAddBo userExamGoodsSupplementAddBo = new UserExamGoodsSupplementAddBo();
                    userExamGoodsSupplementAddBo.setGoodsId(goodsVo.getGoodsId());
                    userExamGoodsSupplementAddBo.setUserExamGoodsId(add.getUserExamGoodsId());
                    userExamGoodsSupplementAddBo.setExamNumber(examNumber.getExamNumber());
                    userExamGoodsSupplementAddBo.setDoNumber(examNumber.getDoNumber());
                    userExamGoodsSupplementAddBo.setStatus(3);
                    userExamGoodsSupplementService.insertByAddBo(userExamGoodsSupplementAddBo);
                }
            }
        }
        //增加前培次数
        if (goodsVo.getGoodsType().equals(4)){
            UserExamGoodsQueryBo userExamGoodsQueryBo = new UserExamGoodsQueryBo();
            userExamGoodsQueryBo.setUserId(order.getUserId());
            userExamGoodsQueryBo.setGoodsId(goodsVo.getMakeGoodsId());
            List<UserExamGoodsVo> userExamGoodsVos = userExamGoodsService.queryList(userExamGoodsQueryBo);
            if (!CollectionUtils.isEmpty(userExamGoodsVos)){
                UserExamGoodsVo userExamGoodsVo = userExamGoodsVos.get(0);
                UserExamGoodsSupplementAddBo userExamGoodsSupplementAddBo = new UserExamGoodsSupplementAddBo();
                userExamGoodsSupplementAddBo.setGoodsId(goodsVo.getGoodsId());
                userExamGoodsSupplementAddBo.setUserExamGoodsId(userExamGoodsVo.getUserExamGoodsId());
                userExamGoodsSupplementAddBo.setDoNumber(goodsVo.getStudyCount());
                userExamGoodsSupplementAddBo.setStatus(2);
                userExamGoodsSupplementService.insertByAddBo(userExamGoodsSupplementAddBo);
                UserExamGoods userExamGoods = new UserExamGoods();
                userExamGoods.setUserExamGoodsId(userExamGoodsVo.getUserExamGoodsId());
                userExamGoods.setDoNumber(userExamGoodsVo.getDoNumber()+goodsVo.getStudyCount());
                userExamGoodsService.updateById(userExamGoods);
            }
        }
        //增加考试次数
        if (goodsVo.getGoodsType().equals(3)){
            UserExamGoodsQueryBo userExamGoodsQueryBo = new UserExamGoodsQueryBo();
            userExamGoodsQueryBo.setUserId(order.getUserId());
            userExamGoodsQueryBo.setGoodsId(goodsVo.getMakeGoodsId());
            List<UserExamGoodsVo> userExamGoodsVos = userExamGoodsService.queryList(userExamGoodsQueryBo);
            if (!CollectionUtils.isEmpty(userExamGoodsVos)){
                UserExamGoodsVo userExamGoodsVo = userExamGoodsVos.get(0);
                UserExamGoodsSupplementAddBo userExamGoodsSupplementAddBo = new UserExamGoodsSupplementAddBo();
                userExamGoodsSupplementAddBo.setGoodsId(goodsVo.getGoodsId());
                userExamGoodsSupplementAddBo.setUserExamGoodsId(userExamGoodsVo.getUserExamGoodsId());
                userExamGoodsSupplementAddBo.setExamNumber(goodsVo.getStudyCount());
                userExamGoodsSupplementAddBo.setStatus(1);
                userExamGoodsSupplementService.insertByAddBo(userExamGoodsSupplementAddBo);
                UserExamGoods userExamGoods = new UserExamGoods();
                userExamGoods.setUserExamGoodsId(userExamGoodsVo.getUserExamGoodsId());
                userExamGoods.setExamNumber(userExamGoodsVo.getExamNumber()+goodsVo.getStudyCount());
                userExamGoodsService.updateById(userExamGoods);
            }
        }
    }

    //加入锁定班级
    @Override
    public boolean joinLockGrade(String orderSn,Long goodsId,Long orderGoodsId){
        LambdaQueryWrapper<ClassGradeUserTemp> lqwLock = new LambdaQueryWrapper<>();
        lqwLock.eq(ClassGradeUserTemp::getOrderSn,orderSn);
        lqwLock.eq(ClassGradeUserTemp::getGoodsId,goodsId);
        ClassGradeUserTemp classGradeUserTemp = iClassGradeUserTempService.getOne(lqwLock);
        if(classGradeUserTemp!=null&&classGradeUserTemp.getStatus()==ClassGradeUserTemp.STATUS_LOCK){
            classGradeUserTemp.setStatus(ClassGradeUserTemp.STATUS_USE); //
            classGradeUserTemp.setUpdateTime(DateUtils.getNowTime());
            iClassGradeUserTempService.updateById(classGradeUserTemp);//关闭锁定状态
            iOrderService.joinGrade(orderGoodsId,classGradeUserTemp.getGradeId(),classGradeUserTemp.getUserId());
        }
        return true;
    }

}
