package com.pj.api.wx;

import cn.hutool.core.net.Ipv4Util;
import cn.hutool.core.net.URLDecoder;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.pj.current.config.MyConfig;
import com.pj.current.config.WxConfig;
import com.pj.project.tb_business.TbBusiness;
import com.pj.project.tb_business.TbBusinessService;
import com.pj.project.tb_business_car.TbBusinessCar;
import com.pj.project.tb_business_car.TbBusinessCarService;
import com.pj.project.tb_charge_record.TbChargeRecordService;
import com.pj.project.tb_costomer.TbCostomer;
import com.pj.project.tb_costomer.TbCostomerService;
import com.pj.project.tb_pay_record.TbPayRecord;
import com.pj.project.tb_pay_record.TbPayRecordService;
import com.pj.utils.cache.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.util.*;

@Service
@Transactional
@Slf4j
public class WxService {
    @Resource
    @Lazy
    private TbBusinessService tbBusinessService;
    @Resource
    WxConfig wxConfig;
    @Resource
    MyConfig myConfig;

    @Resource
    WxUtils wxUtils;
    @Resource
    TbPayRecordService tbPayRecordService;
    @Resource
    TbCostomerService tbCostomerService;
    @Resource
    TbBusinessCarService tbBusinessCarService;

    /**
     * 统一下单接口
     */
    public Map<String, String> prePay(HttpServletRequest request) throws Exception {
        String tradeType = request.getParameter("tradeType");
        String money = request.getParameter("money");
        String carIds = request.getParameter("carId");
        String openid = request.getParameter("openid");
        String businessId = request.getParameter("businessId");
        Attach atchMap = new Attach();
        atchMap.setC(carIds).setB(businessId);
        String out_trade_no = RandomUtil.randomString(32);
        if (StrUtil.isNotEmpty(businessId)) {
            TbBusiness tbBusiness = tbBusinessService.getById(businessId);
            out_trade_no = tbBusiness.getNo();
        } else {
            for (String id : StrUtil.splitTrim(carIds, ",")) {
                TbBusinessCar car = tbBusinessCarService.getById(id);
                out_trade_no = "c_" + car.getId();
                break;
            }
        }
        Map<String, String> params = new HashMap<>();
        params.put("appid", wxConfig.getAppId());
        params.put("mch_id", wxConfig.getMachId());
        params.put("openid", openid);
        params.put("nonce_str", RandomUtil.randomString(32));
        params.put("body", "支付中心-业务订单支付");
        params.put("out_trade_no", out_trade_no+"_"+System.currentTimeMillis()/1000);
        params.put("attach", JSONUtil.toJsonStr(atchMap));
//        String total_free = Integer.parseInt(money) * 100 + "";
        String total_free = "1";
        log.info("pay free:{}", total_free);
        params.put("total_fee", total_free);
        // params.put("total_fee", "1");
        params.put("spbill_create_ip", getIpAddress(request));
        params.put("notify_url", myConfig.getDomain() + "/wx/notify");
        params.put("trade_type", tradeType);
        params.put("scene_info", SceneInfoBO.getSceneInfo(myConfig.getDomain(), wxConfig.getTitle()));
        String sign = wxUtils.sign(params, wxConfig.getKey());
        log.info("wx pay sign result:{}", sign);
        params.put("sign", sign);
        String prePayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        String xmlParam = XmlUtil.mapToXmlStr(params);
        log.info("wx pay xml params:{}", xmlParam);
        String result = HttpUtil.createPost(prePayUrl).header("Content-Type", "text/xml").body(xmlParam)
                .execute().body();
        Map<String, Object> resultMap = XmlUtil.xmlToMap(result);
        log.info("request pre pay url result:{}", resultMap);
        PrePayVO vo = new PrePayVO();
        if ("SUCCESS".equals(resultMap.get("return_code")) && "SUCCESS".equals(resultMap.get("result_code"))) {
            log.info("生成预支付订单成功,mweb_url:{}", resultMap.get("mweb_url"));
            Object webUrl = resultMap.get("mweb_url");
            Object prePayId = resultMap.get("prepay_id");
            String preId = prePayId == null ? "" : prePayId.toString();
            vo.setPayUrl(webUrl == null ? "" : webUrl.toString())
                    .setPrePayId(preId)
                    .setTradeType(tradeType);
            return getPayParams(openid, preId);
        }
        throw new Exception("支付信息有误");
    }

    public Map<String, String> getPayParams(String openid, String prePayId) {
        Map<String, String> signMap = new HashMap<>(10);
        signMap.put("appId", wxConfig.getAppId());
        signMap.put("timeStamp", System.currentTimeMillis() / 1000 + "");
        signMap.put("nonceStr", RandomUtil.randomString(32));
        signMap.put("signType", "MD5");
        signMap.put("package", "prepay_id=" + prePayId);
        String sign = wxUtils.sign(signMap, wxConfig.getKey());
        signMap.put("paySign", sign);
        signMap.put("openId", openid);
        return signMap;
    }

    public static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip.contains(",")) {
            return ip.split(",")[0];
        } else {
            return ip;
        }
    }

    public void WxNotify(HttpServletRequest request) throws Exception {
        Map<String, Object> result = wxUtils.msgToMap(request);
        String total_fee = result.get("total_fee").toString();
        BigDecimal money = new BigDecimal(total_fee).divide(new BigDecimal(100), 2, BigDecimal.ROUND_UP);
        log.info("wx pay notify;{}", JSONUtil.toJsonStr(result));
        String tradeNo = result.get("out_trade_no").toString();
        tradeNo=StrUtil.subBefore(tradeNo,"_",true);
        String attachStr = result.get("attach") == null ? "" : result.get("attach").toString();
        String transactionId = result.get("transaction_id").toString();
        Date now = new Date();
        if (StrUtil.isNotEmpty(attachStr)) {
            Attach attach = JSONUtil.toBean(attachStr, Attach.class);
            String businessId = attach.getB();
            if (StrUtil.isNotEmpty(businessId)) {
                TbBusiness tbBusiness = tbBusinessService.getById(businessId);
                if (tbBusiness.getPayStatus() == 2) {
                    return;
                }
                tbBusiness.setPayTime(now).setPayType(3)
                        .setPayStatus(3).setAdminConfirmPay(1)
                        .setConfirmInput(1).setConfirmInputTime(now)
                        .setPayNo(transactionId);
                tbBusinessService.updateById(tbBusiness);
                List<TbBusinessCar> cars = tbBusinessCarService.findByBusinessId(businessId);
                cars.forEach(tbBusinessCar -> tbBusinessCar.setPay(1).setPayTime(now).setMoney(tbBusinessCar.getBasePartMoney()));
                tbBusinessCarService.updateBatchById(cars);
            }
            String carIds = attach.getC();
            if (StrUtil.isNotEmpty(carIds) && StrUtil.isEmpty(businessId)) {
                StrUtil.splitTrim(carIds, ",").forEach(id -> {
                    TbBusinessCar car = tbBusinessCarService.getById(id);
                    car.setPay(1);
                    car.setMoney(car.getBasePartMoney().add(money));
                    tbBusinessCarService.updateById(car);
                });
            }
        }
        TbBusiness tbBusiness = tbBusinessService.finByNo(tradeNo);
        if (tradeNo.contains("c_")) {
            String busiCarId = tradeNo.replace("c_", "");
            TbBusinessCar tbBusinessCar = tbBusinessCarService.getById(busiCarId);
            tbBusiness=tbBusinessService.getById(tbBusinessCar.getBusinessId());
        }
        if (tbBusiness != null) {
            TbPayRecord payRecord = new TbPayRecord();
            TbCostomer tbCostomer = tbCostomerService.getById(tbBusiness.getCustomerId());
            payRecord.setCreateTime(now)
                    .setOutTradeNo(tradeNo)
                    .setTransactionId(transactionId)
                    .setPayMoney(money)
                    .setCustomerId(tbCostomer.getId()).setCustomerName(tbCostomer.getName());
            tbPayRecordService.save(payRecord);
        }
    }

    public String getRedirectUrl(String path, String state) {
        String redirectUrl = myConfig.getWebDomain() + path;
        String encoderUrl = URLDecoder.decode(redirectUrl, Charset.forName("utf-8"));
        String url = wxConfig.getAuthLoginUrl().replace("REDIRECT_URI", encoderUrl);
        if (StrUtil.isNotEmpty(state)) {
            url = url.replace("STATE", state);
        }
        log.info("get redirect url:{}", url);
        return url;
    }

    public String getOpenidByCode(String code, String openid) {
        String openidUrl = wxConfig.getOpenidUrl().replace("CODE", code);
        String resp = HttpUtil.get(openidUrl);
        JSONObject result = JSONUtil.parseObj(resp);
        log.info("get openid result:{}", resp);
        String newOpenid = result.getStr("openid");
        if (StrUtil.isNotEmpty(newOpenid)) {
            return newOpenid;
        }
        return openid;
    }

    public SortedMap getWxConfig(String url) {
        Long nowTime = System.currentTimeMillis() / 1000;
        String jsTicket = RedisUtil.get(wxConfig.getJsApiTicketKey());
        SortedMap<String, Object> params = new TreeMap<>();
        params.put("noncestr", RandomUtil.randomString(32));
        params.put("jsapi_ticket", jsTicket);
        params.put("timestamp", nowTime);
        params.put("url", url);
        params.put("sign", DigestUtils.sha1Hex(params.toString().substring(1, params.toString().lastIndexOf("}")).replaceAll(", ", "&")));
        params.put("appId", wxConfig.getAppId());
        return params;
    }
}
