package com.ylx.massage.service.impl;


import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.RequestMethodEnum;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.core.utils.DateTimeZoneUtil;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxDomainEnum;
import com.ijpay.wxpay.enums.v3.BasePayApiEnum;
import com.ijpay.wxpay.model.CloseOrderModel;
import com.ijpay.wxpay.model.v3.*;
import com.ylx.common.config.WechatAccountConfig;
import com.ylx.common.config.WxPayConfig;
import com.ylx.common.core.domain.R;
import com.ylx.common.exception.ServiceException;
import com.ylx.common.utils.StringUtils;
import com.ylx.massage.domain.TConsumptionLog;
import com.ylx.massage.domain.TRecharge;
import com.ylx.massage.domain.TWxUser;
import com.ylx.massage.enums.BillTypeEnum;
import com.ylx.massage.mapper.TRechargeMapper;
import com.ylx.massage.service.TConsumptionLogService;
import com.ylx.massage.service.TRechargeService;
import com.ylx.massage.service.TWxUserService;
import com.ylx.massage.utils.OrderNumberGenerator;
import com.ylx.point.service.IPointUserActivityTaskCompletionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.security.cert.X509Certificate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static com.ylx.common.constant.HttpStatus.SUCCESS;


/**
 * 充值记录表 服务实现类
 */
@Service
@Slf4j
public class TRechargeServiceImpl extends ServiceImpl<TRechargeMapper, TRecharge> implements TRechargeService {

    @Resource
    private TWxUserService wxUserService;

    @Resource
    private OrderNumberGenerator generator;

    @Resource
    private TConsumptionLogService consumptionLogService;

    @Autowired
    private WxPayConfig wxPayProperties;

    @Autowired
    private WechatAccountConfig wechatAccountConfig;

    @Autowired
    private TRechargeMapper rechargeMapper;

    @Resource
    private IPointUserActivityTaskCompletionService pointUserActivityTaskCompletionService;

    String serialNo;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TRecharge recharge(TRecharge recharge) {
        //增加充值记录
        if (StringUtils.isBlank(recharge.getcOpenId())) {
            throw new ServiceException("openId不能为空");
        }
        if (null == recharge.getdMoney() || recharge.getdMoney().compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("充值金额不能为空");
        }
//        LambdaQueryWrapper<TRecharge> queryWrapper = new LambdaQueryWrapper<>();
//        queryWrapper.eq(TRecharge::getcOpenId, recharge.getcOpenId()).eq(TRecharge::getPayStatus, -1).eq(TRecharge::getdMoney, recharge.getdMoney());
//        TRecharge one = getOne(queryWrapper);
//        if (one != null) {
//            log.info("未支付的充值记录,{}",one.getRechargeNo());
//            return one;
//        }
        recharge.setRechargeNo(generator.generateNextOrderNumber(OrderNumberGenerator.KEY_PREFIX_RECHAR));
        recharge.setDtCreateTime(LocalDateTime.now());
        save(recharge);
        //todo 调用微信支付成功后增加账户金额
        return recharge;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TRecharge increaseAmount(String rechargeNo) {
        //增加个人账户金额
        TRecharge recharge = getByRechargeNo(rechargeNo);
        if(recharge == null){
            log.error("未支付的充值记录不存在,单号:{}",rechargeNo);
            return null;
        }
        //修改充值状态
        recharge.setPayStatus(1);
        this.updateById(recharge);
        //增加个人账户金额
        TWxUser user = wxUserService.getByOpenId(recharge.getcOpenId());
        user.setdBalance(user.getdBalance().add(recharge.getdMoney()));
        wxUserService.updateById(user);
        //增加消费记录
        TConsumptionLog tConsumptionLog = new TConsumptionLog();
        tConsumptionLog.setAmount(recharge.getdMoney());
        tConsumptionLog.setBillNo(rechargeNo);
        tConsumptionLog.setOpenId(recharge.getcOpenId());
        tConsumptionLog.setBillType(BillTypeEnum.RECHARGE.getCode());
        tConsumptionLog.setNote("微信充值");
        consumptionLogService.save(tConsumptionLog);
        log.info("微信充值成功,充值单号:{},充值金额:{},充值OpenId:{}",rechargeNo,recharge.getdMoney(),user.getcOpenid());
        // 完成积分任务（按照新手活动、每日活动、每月活动的优先级顺序）
        try {
            this.pointUserActivityTaskCompletionService.completeRechargeTaskByPriority(recharge.getcOpenId());
        } catch (Exception e) {
            log.error("完成充值任务失败 - 充值单号：{}, 错误信息：{}", recharge.getRechargeNo(), e.getMessage(), e);
        }
        return recharge;
    }

    @Override
    public R getPay(String setOutTradeNo, BigDecimal amount, String openId, String description, String attach) throws Exception {
        String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
        UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
                .setAppid(wechatAccountConfig.getMpAppId())
                .setMchid(wxPayProperties.getMchId())
                //商品描述
                .setDescription(description)
                //订单号
                .setOut_trade_no(setOutTradeNo)
                //交易结束时间
                .setTime_expire(timeExpire)
                //附加数据
                .setAttach(attach)
                //通知地址异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数。 公网域名必须为https，如果是走专线接入，使用专线NAT IP或者私有回调域名可使用http
                //示例值：https://www.weixin.qq.com/wxpay/pay.php
                .setNotify_url(wxPayProperties.getNotifyUrl())
                //支付金额以分为单位
                .setAmount(new Amount().setTotal(amount.multiply(new BigDecimal(100)).intValue()))
                //交易人
                .setPayer(new Payer().setOpenid(openId));

        log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
        IJPayHttpResponse response = WxPayApi.v3(
                RequestMethodEnum.POST,
                WxDomainEnum.CHINA.toString(),
                BasePayApiEnum.JS_API_PAY.toString(),
                wxPayProperties.getMchId(),
                getSerialNumber(),
                null,
                wxPayProperties.getCertKeyPath(),
                JSONUtil.toJsonStr(unifiedOrderModel)
        );

        log.info("统一下单响应 {}", response);
        // 根据证书序列号查询对应的证书来验证签名结果
        boolean verifySignature = WxPayKit.verifySignature(response, wxPayProperties.getPlatFormPath());
        log.info("verifySignature: {}", verifySignature);
        if (response.getStatus() == SUCCESS && verifySignature) {
            String body = response.getBody();
            JSONObject jsonObject = JSONUtil.parseObj(body);
            String prepayId = jsonObject.getStr("prepay_id");
            Map<String, String> map = WxPayKit.jsApiCreateSign(wechatAccountConfig.getMpAppId(), prepayId, wxPayProperties.getCertKeyPath());
            log.info("唤起支付参数:{}", map);
            return R.ok(JSONUtil.toJsonStr(map));
        }
        return R.ok(JSONUtil.toJsonStr(response));
    }

    @Override
    public String refund(String outRefundNo, String transactionId, String outTradeNo, BigDecimal amount) {
        try {
            int i = amount.multiply(new BigDecimal(100)).intValue();
            log.info("商户退款单号: {}", outRefundNo);
            //退款商品
            List<RefundGoodsDetail> list = new ArrayList<>();
            RefundGoodsDetail refundGoodsDetail = new RefundGoodsDetail()
                    //商户侧商品编码
                    .setMerchant_goods_id("123")
                    //商品名称
                    .setGoods_name("IJPay 测试")
                    //商品单价
                    .setUnit_price(1)
                    //商品退款金额
                    .setRefund_amount(1)
                    //商品退货数量
                    .setRefund_quantity(1);
            list.add(refundGoodsDetail);

            RefundModel refundModel = new RefundModel()
                    //商户退款单号
                    .setOut_refund_no(outRefundNo)
                    //退款原因
                    //.setReason("IJPay 测试退款")
                    //回调地址
                    .setNotify_url(wxPayProperties.getRefundNotify())
                    //金额信息
                    .setAmount(new RefundAmount().setRefund(i).setTotal(i).setCurrency("CNY"));
                    //退款商品
                    //.setGoods_detail(list);

            //微信支付订单号
            if (StrUtil.isNotEmpty(transactionId)) {
                refundModel.setTransaction_id(transactionId);
            }
            //商户订单号
            if (StrUtil.isNotEmpty(outTradeNo)) {
                refundModel.setOut_trade_no(outTradeNo);
            }
            log.info("退款参数 {}", JSONUtil.toJsonStr(refundModel));
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.POST,
                    WxDomainEnum.CHINA.toString(),
                    BasePayApiEnum.REFUND.toString(),
                    wxPayProperties.getMchId(),
                    getSerialNumber(),
                    null,
                    wxPayProperties.getCertKeyPath(),
                    JSONUtil.toJsonStr(refundModel)
            );
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, wxPayProperties.getPlatFormPath());
            log.info("verifySignature: {}", verifySignature);
            log.info("退款响应 {}", response);

            if (verifySignature) {
                return response.getBody();
            }
        } catch (Exception e) {
            log.error("系统异常", e);
            return e.getMessage();
        }
        return null;
    }

    @Override
    public BigDecimal getRecharge(String jsid, Date startDate, Date endDate) {
        return rechargeMapper.getRecharge(jsid, startDate, endDate);
    }

    @Override
    public boolean closeWeChatOrder(String outTradeNo) {
        try {
            // 关闭订单API路径：/v3/pay/transactions/out-trade-no/{out_trade_no}/close
            String closeOrderApi = String.format("/v3/pay/transactions/out-trade-no/%s/close", outTradeNo);
            log.info("关闭微信订单，订单号：{}，API路径：{}", outTradeNo, closeOrderApi);
            CloseOrderModel closeOrderModel = new CloseOrderModel();
            closeOrderModel.setMch_id(wxPayProperties.getMchId());
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.POST,
                    WxDomainEnum.CHINA.toString(),
                    closeOrderApi,
                    wxPayProperties.getMchId(),
                    getSerialNumber(),
                    null,
                    wxPayProperties.getCertKeyPath(),
                    JSONUtil.toJsonStr(closeOrderModel)
            );
            log.info("关闭微信订单响应：{}，状态码：{}", response.getBody(), response.getStatus());
            // 200表示成功
            if (response.getStatus() == SUCCESS) {
                return true;
            }
            // 微信侧无订单时返回ORDERNOTEXIST错误码（订单已支付或从未创建），视为关闭成功
            if (response.getStatus() == 400 && response.getBody() != null && response.getBody().contains("ORDERNOTEXIST")) {
                log.info("微信侧无此订单，无需关闭，订单号：{}", outTradeNo);
                return true;
            }
            return false;
        } catch (Exception e) {
            log.error("关闭微信订单异常，订单号：{}，错误：{}", outTradeNo, e.getMessage(), e);
            // 微信关单失败不影响后续流程，忽略错误
            return true;
        }
    }

    @Override
    public int countSuccessRecharges(String openId, Date rechargeQueryTime) {
        // 统计用户在指定时间起（含）之后成功的充值次数
        // 支付状态为 1 表示已支付（充值成功）
        if (StrUtil.isBlank(openId)) {
            return 0;
        }
        LambdaQueryWrapper<TRecharge> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TRecharge::getcOpenId, openId)
                .eq(TRecharge::getPayStatus, 1);

        // rechargeQueryTime 为 null 表示不限制起点（永久活动/全量统计）
        if (rechargeQueryTime != null) {
            LocalDateTime start = LocalDateTime.ofInstant(rechargeQueryTime.toInstant(), ZoneId.systemDefault());
            queryWrapper.ge(TRecharge::getDtCreateTime, start);
        }

        long cnt = this.count(queryWrapper);
        return cnt > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) cnt;
    }

    /**
     * 获取证书序列号
     * @return String 证书序列号
     */
    private String getSerialNumber() {
        if (StrUtil.isEmpty(serialNo)) {
            // 获取证书序列号
            X509Certificate certificate = PayKit.getCertificate(wxPayProperties.getCertPath());
            if (null != certificate) {
                serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
                // 提前两天检查证书是否有效
                boolean isValid = PayKit.checkCertificateIsValid(certificate, wxPayProperties.getMchId(), -2);
                log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN));
            }
//            System.out.println("输出证书信息:\n" + certificate.toString());
//            // 输出关键信息，截取部分并进行标记
//            System.out.println("证书序列号:" + certificate.getSerialNumber().toString(16));
//            System.out.println("版本号:" + certificate.getVersion());
//            System.out.println("签发者：" + certificate.getIssuerDN());
//            System.out.println("有效起始日期：" + certificate.getNotBefore());
//            System.out.println("有效终止日期：" + certificate.getNotAfter());
//            System.out.println("主体名：" + certificate.getSubjectDN());
//            System.out.println("签名算法：" + certificate.getSigAlgName());
//            System.out.println("签名：" + certificate.getSignature().toString());
        }
        System.out.println("serialNo:" + serialNo);
        return serialNo;
    }

    private TRecharge getByRechargeNo(String rechargeNo) {
        return getOne(new LambdaQueryWrapper<TRecharge>().eq(TRecharge::getRechargeNo, rechargeNo).eq(TRecharge::getPayStatus, -1));
    }
}
