package org.jeecg.modules.app.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.aliyun.oss.ServiceException;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.modules.pay.config.WechatConstants;
import org.jeecg.modules.pay.config.WechatPayV3Utils;
import org.jeecg.modules.pay.config.WechatUrlConstants;
import org.jeecg.modules.pay.serverPay.WXPayUtility;
import org.jeecg.modules.system.app.entity.AppOrder;
import org.jeecg.modules.system.app.entity.AppOrderProInfo;
import org.jeecg.modules.system.app.entity.AppOrderRefundsInfo;
import org.jeecg.modules.system.app.mapper.AppOrderMapper;
import org.jeecg.modules.system.app.mapper.AppOrderProInfoMapper;
import org.jeecg.modules.system.app.mapper.AppOrderRefundsInfoMapper;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author wangzhiqiang
 * @Date 2025/8/21
 * @Desc 微信支付相关服务接口
 */
@Slf4j
@Service
public class WeChatPayService {

    @Resource
    private WechatPayV3Utils wechatPayV3Utils;

    @Resource
    private AppOrderMapper appOrderMapper;

    @Resource
    private AppOrderProInfoMapper appOrderProInfoMapper;

    @Resource
    private AppOrderRefundsInfoMapper appOrderRefundsInfoMapper;


    /**
     * 小程序支付拉起
     *
     * @param params
     * @return
     * @throws ServiceException
     */
    public Map<String, String> wechatPay(JSONObject params) throws ServiceException {

        //发起请求
        JSONObject res = wechatPayV3Utils.sendPost(WechatUrlConstants.PAY_V3_JSAPI, params);
        log.info("wechatPay res:{}", res.toString());
        if (StrUtil.isEmpty(res.getString("prepay_id"))) {
            throw new ServiceException("支付发起失败");
        }

        StringBuilder sb = new StringBuilder();
        //返回给小程序拉起微信支付的参数
        Map<String, String> result = new HashMap<>();
        result.put("appId", WechatConstants.WECHAT_SUB_APPID); //小程序appid
        sb.append(result.get("appId")).append("\n");
        result.put("timeStamp", (new Date().getTime() / 1000) + ""); //时间戳
        sb.append(result.get("timeStamp")).append("\n");
        result.put("nonceStr", RandomStringUtils.randomAlphanumeric(32)); //32位随机字符串
        sb.append(result.get("nonceStr")).append("\n");
        result.put("package", "prepay_id=" + res.getString("prepay_id")); //预支付id 格式为 prepay_id=xxx
        sb.append(result.get("package")).append("\n");
        //签名
        result.put("paySign", wechatPayV3Utils.signRSA(sb.toString()));
        result.put("signType", "RSA"); //加密方式 固定RSA
        result.put("out_trade_no", params.getString("out_trade_no")); //商户订单号 此参数不是小程序拉起支付所需的参数 因此不参与签名

        return result;
    }

//    public static void main(String[] args) {
//        String s = "{\"mchid\":\"1523499681\",\"appid\":\"wx9b0396a7507e3d66\",\"out_trade_no\":\"4022961176\"," +
//                "\"transaction_id\":\"4200001859202306051015832415\",\"trade_type\":\"JSAPI\",\"trade_state\":\"SUCCESS\",\"trade_state_desc\":\"支付成功\",\"bank_type\":\"ICBC_DEBIT\",\"attach\":\"4022961176\",\"success_time\":\"2023-06-05T14:45:20+08:00\",\"payer\":{\"openid\":\"ok_fR5NmcrTnxFwiSlyd58_TuXrU\"},\"amount\":{\"total\":1,\"payer_total\":1,\"currency\":\"CNY\",\"payer_currency\":\"CNY\"}}";
//        JSONObject res = JSONObject.parseObject(s);
//        String payer_total = res.getJSONObject("amount").getString("payer_total");
//        BigDecimal bigDecimal = new BigDecimal(payer_total);
//        System.out.println(dealDateFormat(res.get("success_time").toString()));
//        //获取当前日期
//        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
//
//        String format = sdf.format(DateUtils.getNowDate());
//
//        System.out.println(format);
//    }

    public static Date dealDateFormat(String oldDateStr) {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
        try {
            Date date = df.parse(oldDateStr);
            return date;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 测试微信支付有优惠情况的数据获取
     *
     * @param args
     */
    public static void main(String[] args) {
        String obj = "{\"amount\":{\"currency\":\"CNY\",\"payer_currency\":\"CNY\",\"payer_total\":18300,\"total\":18400}," +
                "\"appid\":\"wx9b0396a7507e3d66\",\"attach\":\"20230915155102291908100\",\"bank_type\":\"PAB_CREDIT\",\"mchid\":\"1523499681\",\"out_trade_no\":\"20230915155102291908100\",\"payer\":{\"openid\":\"ok_fR5N24KGz9zQBCKNoWgzuT57Y\"},\"promotion_detail\":[{\"amount\":100,\"coupon_id\":\"50099133534\",\"currency\":\"CNY\",\"goods_detail\":[],\"merchant_contribute\":0,\"name\":\"平安信用卡9月专属\",\"other_contribute\":100,\"scope\":\"GLOBAL\",\"stock_id\":\"17950154\",\"type\":\"CASH\",\"wechatpay_contribute\":0}],\"success_time\":\"2023-09-15T15:51:23+08:00\",\"trade_state\":\"SUCCESS\",\"trade_state_desc\":\"支付成功\",\"trade_type\":\"JSAPI\",\"transaction_id\":\"4200001916202309158539287225\"}";

        JSONObject res = JSONObject.parseObject(obj);

        String payer_total = res.getJSONObject("amount").getString("payer_total");

        System.out.println(payer_total);

        String openid = res.getJSONObject("payer").getString("openid");

        System.out.println(openid);

        Date successTime = dealDateFormat(res.get("success_time").toString());

        System.out.println(successTime);

        BigDecimal discounts = new BigDecimal("0");

        String discountsName = "";

        if (ObjectUtil.isNotEmpty(res.getJSONArray("promotion_detail"))) {

            JSONArray jsonArray = res.getJSONArray("promotion_detail");

            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);

                if (ObjectUtil.isNotEmpty(jsonObject.getString("amount"))) {
                    discounts =
                            discounts.add(new BigDecimal(jsonObject.getString("amount")).divide(new BigDecimal("100")).setScale(2,
                                    BigDecimal.ROUND_HALF_UP));
                }

                if (ObjectUtil.isNotEmpty(jsonObject.getString("name"))) {
                    if ("".equals(discountsName)) {
                        discountsName = jsonObject.getString("name");
                    } else {
                        discountsName = discountsName + "、" + jsonObject.getString("name");
                    }
                }
                System.out.println(res);
            }
        }

        System.out.println(discountsName);
        System.out.println(res.get("promotion_detail").toString());

    }

    /**
     * 小程序微信支付回调
     *
     * @param request
     * @return
     */
    public Map<String, String> wechatPayNotify(HttpServletRequest request) {
        Map<String, String> result = new HashMap<>(2);
        //验签及解析返回数据
        JSONObject res = wechatPayV3Utils.getCallbackData(request);
        if (res == null) {
            result.put("code", "FAIL");
            result.put("message", "失败");
            return result;
        }
        log.info("最终拿到的微信支付通知数据：" + res);

        // 处理支付成功后的业务 例如 将订单状态修改为已支付 具体参数键值可参考文档 注意！！！ 微信可能会多次发送重复的通知 因此要判断业务是否已经处理过了 避免重复处理
        try {
            String orderCode = res.getString("out_trade_no");
            //查询订单，判断是否已修改为已支付状态
            AppOrder appOrder = appOrderMapper.selectOne(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getOrderCode, orderCode).last("limit 1"));
            if (ObjectUtil.isNotEmpty(appOrder)) {
                if (Objects.equals(appOrder.getOrderStatus(), CommonConstant.ORDER_STATUS_0)){
                    appOrder.setOrderStatus(CommonConstant.ORDER_STATUS_1);
                    appOrder.setPayStatus(CommonConstant.ORDER_STATUS_1);
                    appOrder.setPayTime(new Date());
                    appOrder.setPayType(CommonConstant.STATUS_0_INT);

                    List<AppOrderProInfo> proInfoList = appOrderProInfoMapper.selectList(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId, appOrder.getId()));
                    if (ObjectUtil.isNotEmpty(proInfoList)){
                        for (AppOrderProInfo appOrderProInfo : proInfoList) {
                            appOrderProInfo.setOrderStatus(CommonConstant.ORDER_STATUS_1);
                            appOrderProInfoMapper.updateById(appOrderProInfo);
                        }
                    }
                }
                appOrder.setCallbackStatus(CommonConstant.STATUS_1_INT);
                appOrderMapper.updateById(appOrder);
            }
            result.put("code", "SUCCESS");
            result.put("message", "OK");
            result.put("orderCode",orderCode);
            return result;
        } catch (Exception e) {
            result.put("code", "FAIL");
            result.put("message", "失败");
            return result;
        }
    }

    /**
     * 通过商户订单号查询订单在微信侧支付状态
     *
     * @param out_trade_no 发起支付时创建的商户订单号
     * @return null代表查询失败 SUCCESS-成功 USERPAYING和ACCEPT为中间态 其他为支付失败
     */
    public JSONObject orderQueryByOutTradeNo(String out_trade_no) {
        String url = WechatUrlConstants.PAY_V3_QUERY_OUT;
        url = url.replace("{out_trade_no}", WXPayUtility.urlEncode(out_trade_no));
        Map<String, Object> args = new HashMap<>();
        args.put("sp_mchid", WechatConstants.WECHAT_SP_MCH_ID);
        args.put("sub_mchid", WechatConstants.WECHAT_SUB_MCH_ID);
        url = url + "?" + WXPayUtility.urlEncode(args);
        JSONObject res = wechatPayV3Utils.sendGet(url);
        return res ;
    }

    /**
     * 申请退款
     * @param orderCode 原订单号
     */
    public void refundOrder(String orderCode,String reason){

        log.info("进入退款接口------>");
        log.info("执行操作的 原支付交易对应的商户订单号：{}", orderCode);

        BigDecimal totalFee = BigDecimal.ZERO;
        BigDecimal total = BigDecimal.ZERO;
        AppOrder appOrder = appOrderMapper.selectOne(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getOrderCode, orderCode).last("limit 1"));
        if (ObjectUtil.isNotEmpty(appOrder)) {
            total = appOrder.getPrice();
            totalFee = total;
            List<AppOrderProInfo> proInfoList = appOrderProInfoMapper.selectList(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId, appOrder.getId()).eq(AppOrderProInfo::getType, CommonConstant.ORDER_PRO_INFO_TYPE_6));
            if (ObjectUtil.isNotEmpty(proInfoList)) {
                for (AppOrderProInfo appOrderProInfo : proInfoList) {
                    totalFee = totalFee.subtract(appOrderProInfo.getPrice());
                }
            }
        }
        //退款单号
        String out_refund_no = generateOrderNumber(1);

        // 创建退款订单
        AppOrderRefundsInfo appOrderRefundsInfo = new AppOrderRefundsInfo();

        appOrderRefundsInfo.setOrderId(appOrder.getId());
        appOrderRefundsInfo.setOrderCode(orderCode);
        appOrderRefundsInfo.setOutRefundNo(out_refund_no);
//        appOrderRefundsInfo.setRefundId();
        appOrderRefundsInfo.setTransactionId(appOrder.getTransactionId());
        appOrderRefundsInfo.setReason(reason);
//        appOrderRefundsInfo.setSuccessTime();
        appOrderRefundsInfo.setAmount(total);
//        appOrderRefundsInfo.setNotifyRequest();
        appOrderRefundsInfo.setCreateTime(new Date());

        appOrderRefundsInfoMapper.insert(appOrderRefundsInfo);


        //todo 发起分账回退

        try {
            JSONObject params = new JSONObject();
            params.put("sub_mchid", WechatConstants.WECHAT_SUB_MCH_ID);
            params.put("out_trade_no", orderCode);//商户订单号
            params.put("out_refund_no", out_refund_no);//商户退款单号
            params.put("reason", reason);//退款原因
            params.put("notify_url", WechatUrlConstants.PAY_V3_REFUND_NOTIFY);//退款通知

            JSONObject amount = new JSONObject();
            amount.put("refund", (totalFee.multiply(BigDecimal.valueOf(100))).longValue());//退款金额
            amount.put("currency", "CNY");
            amount.put("total", (total.multiply(BigDecimal.valueOf(100))).longValue());//原订单金额
            params.put("amount", amount);
            // 执行请求POST 请求发送到微信退款接口
            JSONObject res = wechatPayV3Utils.sendPost(WechatUrlConstants.PAY_V3_REFUND, params);

            log.info("最终拿到的微信支付通知数据：" + res);

            final String status = res.getString("status");
            switch (status) {
                case "SUCCESS":
                    log.info("订单：{},退款成功！原因：{}",orderCode,reason);
                    break;
                case "CLOSED":
                    log.info("退款关闭");
                    break;
                case "PROCESSING":
                    log.info("退款处理中");

                    break;
                case "ABNORMAL":
                    log.info("订单：{},退款异常",orderCode);
                    break;
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            log.info(e.toString());
            e.printStackTrace();
        }
    }

    /**
     * @return String 订单号
     * @Author SheepHy
     * @Description 订单编号生成逻辑
     * @Date 17:18 2025/7/15
     * @params 类型：0-（D:订单） 1-（T:退单） 2-（B:保单）
     **/
    private String generateOrderNumber(int type) {
        String format = DateUtil.format(new Date(), "yyyyMMddHHmmss");
        int nextInt = ThreadLocalRandom.current().nextInt(1000, 10000);

        if (type == 0) {
            return "D" + format + nextInt;
        } else {
            return "T" + format + nextInt;
        }
    }

    public Map<String, Object> refundOrderNotify(String jsonData) throws IOException, GeneralSecurityException {

        //转为map格式
        Map jsonMap = JSONObject.parseObject(jsonData, Map.class);

        //退款成功后返回一个加密字段resource,以下为解密
        /**
         * 解密需要从resource参数中，获取到ciphertext，nonce，associated_data这三个参数进行解密
         */
        String resource = JSONObject.toJSONString(jsonMap.get("resource"));
        JSONObject object = JSONObject.parseObject(resource);

        String ciphertext = String.valueOf(object.get("ciphertext"));
        String nonce = String.valueOf(object.get("nonce"));
        String associated_data = String.valueOf(object.get("associated_data"));

        String resultStr = decryptToString(associated_data.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
        Map<String, String> reqInfo = JSONObject.parseObject(resultStr, Map.class);

        log.info("微信返回的退款通知数据：" + reqInfo);

        String refund_status = reqInfo.get("refund_status");//退款状态
        String out_trade_no = reqInfo.get("out_trade_no"); //订单号

        Map<String, Object> parm = new HashMap<>();
        if (!StringUtils.isEmpty(refund_status) && "SUCCESS".equals(refund_status))  {

            //查询订单
            AppOrder order = appOrderMapper.selectOne(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getOrderCode, out_trade_no).last("limit 1"));
            if (ObjectUtil.isNotEmpty(order)) {
                if (order.getOrderStatus() == 0) {
                    order.setOrderStatus(CommonConstant.ORDER_STATUS_1);
                    order.setPayStatus(CommonConstant.ORDER_STATUS_1);
                    order.setPayTime(new Date());
                    order.setPayType(CommonConstant.STATUS_0_INT);
                    order.setCallbackStatus(CommonConstant.STATUS_1_INT);
                    appOrderMapper.updateById(order);
                    List<AppOrderProInfo> proInfoList = appOrderProInfoMapper.selectList(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId, order.getId()));
                    if (ObjectUtil.isNotEmpty(proInfoList)) {
                        for (AppOrderProInfo appOrderProInfo : proInfoList) {
                            appOrderProInfo.setOrderStatus(CommonConstant.ORDER_STATUS_1);
                            appOrderProInfoMapper.updateById(appOrderProInfo);
                        }
                    }
                }
            }

            // 创建退款订单
            AppOrderRefundsInfo appOrderRefundsInfo = appOrderRefundsInfoMapper.selectOne(Wrappers.lambdaQuery(AppOrderRefundsInfo.class).eq(AppOrderRefundsInfo::getOrderId, order.getId()));

            if (ObjectUtil.isNotEmpty(appOrderRefundsInfo)){
                //        appOrderRefundsInfo.setRefundId();
                appOrderRefundsInfo.setSuccessTime(new Date());
//        appOrderRefundsInfo.setNotifyRequest();
                appOrderRefundsInfoMapper.updateById(appOrderRefundsInfo);
            }

            parm.put("code", "SUCCESS");
            parm.put("message", "成功");
        } else {
            parm.put("code", "FAIL");
            parm.put("message", "失败");
            throw new RuntimeException("退款失败");
        }
        return parm;  //返回给前端的参数
    }

    //退款回调  解密数据
    public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext) throws GeneralSecurityException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            SecretKeySpec key = new SecretKeySpec(WechatConstants.WECHAT_MCH_SECRET_V3.getBytes(), "AES");// 这里的apiV3key是你的商户APIV3密钥
            GCMParameterSpec spec = new GCMParameterSpec(128, nonce);//规定为128
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);
            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }
}
